diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md index ef4ec48..f350b88 100644 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -24,8 +24,8 @@ ex) 메서드 XXX의 이름을 더 잘 짓고 싶은데 혹시 좋은 명칭이 ## ✅ PR Checklist PR이 다음 요구 사항을 충족하는지 확인하세요. -[ ] 커밋 메시지 컨벤션에 맞게 작성했습니다. -[ ] 변경 사항에 대한 테스트를 했습니다.(버그 수정/기능에 대한 테스트). +- [ ] 커밋 메시지 컨벤션에 맞게 작성했습니다. +- [ ] 변경 사항에 대한 테스트를 했습니다.(버그 수정/기능에 대한 테스트).
Closes 이슈키-이슈번호 \ No newline at end of file diff --git a/.github/workflows/code-analyze-pr.yml b/.github/workflows/code-analyze-pr.yml new file mode 100644 index 0000000..1c18b89 --- /dev/null +++ b/.github/workflows/code-analyze-pr.yml @@ -0,0 +1,55 @@ +name: Code Analyze Pull Request + +run-name: Run code analyze triggered with pull request by ${{github.actor}} + +on: + pull_request: + types: [opened, reopened, synchronize] + branches: + - main + - develop +env: + NODE_VERSION: 18.12.0 + +jobs: + code_analysis: + + runs-on: ubuntu-latest + + steps: + - name: 'Checkout repository on branch: ${{ github.REF }}' + uses: actions/checkout@v3 + with: + ref: ${{ github.HEAD_REF }} + + - name: Retrieve entire repository history + run: | + git fetch --prune --unshallow + + - name: Setup Node.js + uses: actions/setup-node@v3 + with: + node-version: ${{ env.NODE_VERSION }} + + - name: Cache node modules + uses: actions/cache@v3 + id: npm-cache + with: + path: '**/node_modules' + key: ${{ runner.os }}-node-${{ hashFiles('**/package-lock.json') }} + restore-keys: | + ${{ runner.os }}-node- + + - name: Install Dependencies + if: steps.npm-cache.outputs.cache-hit != 'true' + run: npm install + + - name: Coverage Test + continue-on-error: true + run: npm run coverage + + - name: Run an analysis of the ${{ github.REF }} branch ${{ github.BASE_REF }} base + uses: sonarsource/sonarqube-scan-action@master + env: + SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }} + SONAR_HOST_URL: ${{ secrets.SONAR_HOST_URL }} \ No newline at end of file diff --git a/.github/workflows/code-analyze-push.yml b/.github/workflows/code-analyze-push.yml new file mode 100644 index 0000000..36c2072 --- /dev/null +++ b/.github/workflows/code-analyze-push.yml @@ -0,0 +1,48 @@ +name: Code Analyze branch + +run-name: Run code analyze triggered with push by ${{github.actor}} + +on: + push: + branches: + - main + - develop + +jobs: + code_analysis: + runs-on: ubuntu-latest + + steps: + - name: 'Checkout repository on branch: ${{ github.REF }}' + uses: actions/checkout@v3 + with: + ref: ${{ github.REF }} + fetch-depth: 0 + + - name: Setup Node.js + uses: actions/setup-node@v3 + with: + node-version: ${{ env.NODE_VERSION }} + + - name: Cache node modules + uses: actions/cache@v3 + id: npm-cache + with: + path: '**/node_modules' + key: ${{ runner.os }}-node-${{ hashFiles('**/yarn.lock') }} + restore-keys: | + ${{ runner.os }}-node- + + - name: Install Dependencies + if: steps.npm-cache.outputs.cache-hit != 'true' + run: yarn install + + - name: Coverage Test + continue-on-error: true + run: yarn run coverage + + - name: 'Run an analysis of the ${{ github.REF }} branch' + uses: sonarsource/sonarqube-scan-action@master + env: + SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }} + SONAR_HOST_URL: ${{ secrets.SONAR_HOST_URL }} \ No newline at end of file diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index e4cc15b..98ebda9 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -1,6 +1,6 @@ name: Deploy remix app -run-name: Deploy remix app to docker hub by ${{github.actor}} +run-name: Deploy remix app to docker hub and start services by ${{github.actor}} on: push: @@ -19,6 +19,8 @@ jobs: echo "VITE_OAUTH_URL=${{ secrets.VITE_OAUTH_URL }}" > .env echo "VITE_API_URL=${{ secrets.VITE_API_URL }}" >> .env echo "VITE_STOMP_URL=${{ secrets.VITE_STOMP_URL }}" >> .env + echo "VITE_AI_URL=${{ secrets.VITE_AI_URL }}" >> .env + - name: Build image run: | docker build -t ${{secrets.DOCKER_USERNAME}}/if-fe:latest . @@ -29,4 +31,18 @@ jobs: password: ${{secrets.DOCKER_TOKEN}} - name: Push image to Docker hub run: | - docker push ${{secrets.DOCKER_USERNAME}}/if-fe:latest \ No newline at end of file + docker push ${{secrets.DOCKER_USERNAME}}/if-fe:latest + - name: Connect to cloud server + uses: appleboy/ssh-action@v1.2.2 + with: + host: ${{secrets.FRONTEND_HOST}} + username: ${{secrets.FRONTEND_USERNAME}} + key: ${{secrets.FRONTEND_SECRET_KEY}} + port: ${{secrets.FRONTEND_PORT}} + script: | + cd ~ + docker stop frontend || true + docker rm frontend || true + docker rmi ${{secrets.DOCKER_USERNAME}}/if-fe:latest || true + docker pull ${{secrets.DOCKER_USERNAME}}/if-fe:latest + docker run -d --name frontend -p 3000:3000 ${{secrets.DOCKER_USERNAME}}/if-fe:latest \ No newline at end of file diff --git a/.github/workflows/start-services.yml b/.github/workflows/start-services.yml deleted file mode 100644 index 9e69bd2..0000000 --- a/.github/workflows/start-services.yml +++ /dev/null @@ -1,27 +0,0 @@ -name: Start services - -run-name: Start containers in cloud service by ${{ github.actor }} - -on: - workflow_run: - workflows: [Deploy remix app] - types: - - completed - -jobs: - start-services: - runs-on: ubuntu-latest - steps: - - name: Connect to cloud server - uses: appleboy/ssh-action@v1.2.2 - with: - host: ${{secrets.CLOUD_HOST}} - username: ${{secrets.CLOUD_USERNAME}} - key: ${{secrets.CLOUD_SECRET_KEY}} - port: ${{secrets.CLOUD_PORT}} - script: | - cd ~ - sudo docker compose down --volumes - sudo docker container prune -f - sudo docker image prune -a -f - sudo docker compose up -d diff --git a/.gitignore b/.gitignore index 43e0676..0c55d88 100644 --- a/.gitignore +++ b/.gitignore @@ -27,4 +27,5 @@ dist-ssr /.react-router/ /build/ +coverage .env \ No newline at end of file diff --git a/biome.json b/biome.json index 058c00e..534c288 100644 --- a/biome.json +++ b/biome.json @@ -47,7 +47,7 @@ "options": { "strictCase": false, "requireAscii": true, - "filenameCases": ["camelCase", "export"] + "filenameCases": ["PascalCase", "camelCase", "export"] } } }, @@ -60,8 +60,8 @@ { "include": [ "test/**/*", - "*.test.{ts,tsx,js,jsx}", - "*.spec.{ts,tsx,js,jsx}", + "**/*.test.{ts,tsx,js,jsx}", + "**/*.spec.{ts,tsx,js,jsx}", "src/app/routes/*" ], "linter": { diff --git a/package.json b/package.json index e6af6a0..05c9eac 100644 --- a/package.json +++ b/package.json @@ -8,7 +8,9 @@ "build": "react-router build", "start": "react-router-serve ./build/server/index.js", "typecheck": "react-router typegen && tsc", - "lint": "biome check --write --unsafe .", + "test": "vitest", + "coverage": "vitest run --coverage", + "lint": "biome check --write .", "postinstall": "husky" }, "lint-staged": { @@ -38,23 +40,28 @@ "@react-router/dev": "^7.5.3", "@svgr/rollup": "^8.1.0", "@tailwindcss/vite": "^4.1.5", + "@testing-library/dom": "^10.4.0", + "@testing-library/jest-dom": "^6.6.3", + "@testing-library/react": "^16.3.0", + "@testing-library/user-event": "^14.6.1", "@types/node": "^22.15.3", "@types/react": "^19.1.2", "@types/react-dom": "^19.1.3", + "@vitest/coverage-v8": "^3.1.4", "globals": "^16.0.0", "husky": "^9.1.7", + "jsdom": "^26.1.0", "lint-staged": "^15.5.1", "msw": "^2.8.2", "tailwindcss": "^4.1.5", "typescript": "~5.7.2", "vite": "^6.3.1", "vite-plugin-svgr": "^4.3.0", - "vite-tsconfig-paths": "^5.1.4" + "vite-tsconfig-paths": "^5.1.4", + "vitest": "^3.1.4" }, "packageManager": "yarn@1.22.22+sha512.a6b2f7906b721bba3d67d4aff083df04dad64c399707841b7acf00f6b133b7ac24255f2652fa22ae3534329dc6180534e98d17432037ff6fd140556e2bb3137e", "msw": { - "workerDirectory": [ - "public" - ] + "workerDirectory": ["public"] } } diff --git a/setupTests.ts b/setupTests.ts new file mode 100644 index 0000000..7b0828b --- /dev/null +++ b/setupTests.ts @@ -0,0 +1 @@ +import '@testing-library/jest-dom'; diff --git a/sonar-project.properties b/sonar-project.properties new file mode 100644 index 0000000..ffd7492 --- /dev/null +++ b/sonar-project.properties @@ -0,0 +1,30 @@ +# 프로젝트 식별자 +sonar.projectKey=CleanEngine_cleanengine-fe_c6875537-ed9d-4dfe-b79c-ea5cb6d2a0e3 + +# 프로젝트 이름 +sonar.projectName=CleanEngine Frontend + +# 소스 코드 위치 +sonar.sources=src + +# 테스트 파일 위치 및 패턴 +sonar.tests=src +sonar.test.inclusions=**/*.test.tsx,**/*.test.ts,**/*.spec.tsx,**/*.spec.ts + +# TypeScript/JavaScript 설정 +sonar.javascript.lcov.reportPaths=coverage/lcov.info +sonar.typescript.tsconfigPath=tsconfig.json + +# 분석에서 제외할 파일/디렉토리 +sonar.exclusions=node_modules/**,build/**,dist/**,coverage/**,public/**,**/*.test.tsx,**/*.test.ts,**/*.spec.tsx,**/*.spec.ts + +# 코드 중복 검사 제외 +sonar.cpd.exclusions=**/*.test.tsx,**/*.test.ts + +# 언어 설정 +sonar.language=ts +sonar.typescript.file.suffixes=.ts,.tsx +sonar.javascript.file.suffixes=.js,.jsx + +# 커버리지 파일 위치 +sonar.javascript.lcov.reportPaths=./coverage/lcov.info diff --git a/src/app/entry.client.tsx b/src/app/entry.client.tsx index 735cbac..a22e392 100644 --- a/src/app/entry.client.tsx +++ b/src/app/entry.client.tsx @@ -1,3 +1,4 @@ +/* v8 ignore start */ import { StrictMode, startTransition } from 'react'; import { hydrateRoot } from 'react-dom/client'; import { HydratedRouter } from 'react-router/dom'; @@ -21,3 +22,5 @@ prepareApp().then(() => { ); }); }); + +/* v8 ignore end */ diff --git a/src/app/entry.server.tsx b/src/app/entry.server.tsx index cfdbca4..dd18deb 100644 --- a/src/app/entry.server.tsx +++ b/src/app/entry.server.tsx @@ -1,3 +1,4 @@ +/* v8 ignore start */ import { PassThrough } from 'node:stream'; import { createReadableStreamFromReadable } from '@react-router/node'; @@ -73,3 +74,5 @@ export default function handleRequest( setTimeout(abort, streamTimeout + 1000); }); } + +/* v8 ignore end */ diff --git a/src/app/provider/StompProvider.test.tsx b/src/app/provider/StompProvider.test.tsx new file mode 100644 index 0000000..f7619a5 --- /dev/null +++ b/src/app/provider/StompProvider.test.tsx @@ -0,0 +1,36 @@ +import { renderHook } from '@testing-library/react'; +import { describe, expect, it, vi } from 'vitest'; + +import StompProvider, { useStompClient } from './StompProvider'; + +vi.mock('@stomp/stompjs', () => { + return { + Client: vi.fn().mockImplementation(() => { + return { + activate: vi.fn(), + deactivate: vi.fn(), + onConnect: null, + onDisconnect: null, + onWebSocketError: null, + onStompError: null, + }; + }), + }; +}); + +describe('useStompClient 테스트', () => { + it('useStompClient hook은 StompProvider 외부에서 사용하면 에러를 던진다.', () => { + expect(() => renderHook(() => useStompClient())).toThrowError(); + }); + + it('useStompClient hook은 StompProvider 내부에서 사용하면 정상 작동한다.', () => { + const { result } = renderHook(() => useStompClient(), { + wrapper: ({ children }) => ( + {children} + ), + }); + + expect(result.current).toHaveProperty('client'); + expect(result.current).toHaveProperty('connected'); + }); +}); diff --git a/src/app/provider/StompProvider.tsx b/src/app/provider/StompProvider.tsx index 45fbe37..d711d3e 100644 --- a/src/app/provider/StompProvider.tsx +++ b/src/app/provider/StompProvider.tsx @@ -11,10 +11,7 @@ type StompContextType = { connected: boolean; }; -export const StompContext = createContext({ - client: null, - connected: false, -}); +export const StompContext = createContext(null); export default function StompProvider({ children, @@ -69,6 +66,13 @@ export default function StompProvider({ } export function useStompClient() { - const { client, connected } = useContext(StompContext); - return { client, connected }; + const stompContext = useContext(StompContext); + + if (!stompContext) { + throw new Error( + 'useStompClient hook은 StompProvider 내부에서 사용해야 합니다.', + ); + } + + return { client: stompContext.client, connected: stompContext.connected }; } diff --git a/src/app/routes.ts b/src/app/routes.ts index 6fb6b54..8e87801 100644 --- a/src/app/routes.ts +++ b/src/app/routes.ts @@ -1,4 +1,6 @@ +/* v8 ignore start */ import type { RouteConfig } from '@react-router/dev/routes'; import { flatRoutes } from '@react-router/fs-routes'; export default flatRoutes() satisfies RouteConfig; +/* v8 ignore end */ diff --git a/src/entities/coin/api/coin.endpoint.ts b/src/entities/coin/api/coin.endpoint.ts index ee8907f..c252145 100644 --- a/src/entities/coin/api/coin.endpoint.ts +++ b/src/entities/coin/api/coin.endpoint.ts @@ -1,3 +1,4 @@ +/* v8 ignore start */ import httpClient from '~/shared/api/httpClient'; import type { CoinListResponse } from '../types/coin.type'; @@ -6,3 +7,4 @@ export default { return httpClient.get('api/asset'); }, }; +/* v8 ignore end */ diff --git a/src/entities/coin/hooks/useCurrentPrice.tsx b/src/entities/coin/hooks/useCurrentPrice.tsx index 642f98d..03406f8 100644 --- a/src/entities/coin/hooks/useCurrentPrice.tsx +++ b/src/entities/coin/hooks/useCurrentPrice.tsx @@ -1,7 +1,7 @@ import { useEffect, useState } from 'react'; import { useStompClient } from '~/app/provider/StompProvider'; -type CurrentPriceData = { +export type CurrentPriceData = { changeRate: number; currentPrice: number; prevClose: number; diff --git a/src/entities/coin/index.ts b/src/entities/coin/index.ts index e7989e7..ed28aec 100644 --- a/src/entities/coin/index.ts +++ b/src/entities/coin/index.ts @@ -1,3 +1,4 @@ +/* v8 ignore start */ export { default as CoinWithIconAndName } from './ui/CoinWithIconAndName'; export type { CoinWithIconAndNameProps } from './ui/CoinWithIconAndName'; export { default as useCurrentPrice } from './hooks/useCurrentPrice'; @@ -10,3 +11,4 @@ export type { CoinName, CoinTicker, } from './types/coin.type'; +/* v8 ignore end */ diff --git a/src/entities/coin/ui/CoinPriceWithName/CoinPriceWithName.test.tsx b/src/entities/coin/ui/CoinPriceWithName/CoinPriceWithName.test.tsx new file mode 100644 index 0000000..ed88c23 --- /dev/null +++ b/src/entities/coin/ui/CoinPriceWithName/CoinPriceWithName.test.tsx @@ -0,0 +1,63 @@ +import { render, screen } from '@testing-library/react'; +import { describe, expect, it, vi } from 'vitest'; +import CloudImage from '~/assets/images/cloud.webp'; + +import '../../hooks/useCurrentPrice'; +import CoinPriceWithName from '.'; + +const mockPriceData = { + changeRate: 4, + currentPrice: 100_000_000, + prevClose: 100_000_000, + ticker: 'BTC', + timestamp: '2025-05-30T15:00:00.000Z', +}; + +vi.mock('../../hooks/useCurrentPrice', () => ({ + default: vi.fn().mockImplementation((ticker) => { + return mockPriceData; + }), +})); + +describe('CoinPriceWithName 컴포넌트 테스트', () => { + it('name과 ticker과 img가 prop으로 전달되면 화면에 보인다', () => { + const props = { + name: '비트코인', + ticker: 'BTC', + img: CloudImage, + }; + render(); + + const name = screen.getByText('비트코인'); + const ticker = screen.getByText('BTC'); + const img = screen.getByRole('img'); + + expect(name).toBeInTheDocument(); + expect(ticker).toBeInTheDocument(); + expect(img).toBeInTheDocument(); + }); + + it('img가 prop으로 전달되지 않으면 대체 아이콘이 보인다', () => { + const props = { + name: '비트코인', + ticker: 'BTC', + img: undefined, + }; + render(); + + const img = screen.getByText('🪙'); + expect(img).toBeInTheDocument(); + }); + + it('ticker가 주어지면 해당하는 코인의 가격이 한국의 원화 형식에 맞게 화면에 보인다.', () => { + const props = { + name: '비트코인', + ticker: 'BTC', + img: CloudImage, + }; + render(); + + const price = screen.getByText('100,000,000원'); + expect(price).toBeInTheDocument(); + }); +}); diff --git a/src/entities/coin/ui/CoinWithIconAndName/CoinWithIconAndName.test.tsx b/src/entities/coin/ui/CoinWithIconAndName/CoinWithIconAndName.test.tsx new file mode 100644 index 0000000..9cf771c --- /dev/null +++ b/src/entities/coin/ui/CoinWithIconAndName/CoinWithIconAndName.test.tsx @@ -0,0 +1,26 @@ +import { render, screen } from '@testing-library/react'; +import { describe, expect, it } from 'vitest'; +import CoinWithIconAndName from '.'; + +describe('CoinWithIconAndName 컴포넌트 테스트', () => { + it('props로 전달된 name, ticker, coinIcon이 렌더링 된다 .', () => { + const props = { + name: '비트코인', + ticker: 'BTC', + coinIcon: 🪙, + }; + render(); + + const component = screen.getByTestId('coin-with-icon-and-name'); + expect(component).toBeInTheDocument(); + + const coinIcon = screen.getByText('🪙'); + expect(coinIcon).toBeInTheDocument(); + + const ticker = screen.getByText('BTC'); + expect(ticker).toBeInTheDocument(); + + const name = screen.getByText('비트코인'); + expect(name).toBeInTheDocument(); + }); +}); diff --git a/src/entities/coin/ui/CoinWithIconAndName/index.tsx b/src/entities/coin/ui/CoinWithIconAndName/index.tsx index 6ca0e41..5e33017 100644 --- a/src/entities/coin/ui/CoinWithIconAndName/index.tsx +++ b/src/entities/coin/ui/CoinWithIconAndName/index.tsx @@ -11,7 +11,7 @@ export default function CoinWithIconAndName({ coinIcon, }: CoinWithIconAndNameProps) { return ( -
+
{coinIcon} diff --git a/src/entities/order/index.ts b/src/entities/order/index.ts index ffeced0..039bf71 100644 --- a/src/entities/order/index.ts +++ b/src/entities/order/index.ts @@ -1 +1,3 @@ +/* v8 ignore start */ export { default as QuantityInput } from './ui/QuantityInput'; +/* v8 ignore end */ diff --git a/src/entities/order/ui/QuantityInput/QuantityInput.test.tsx b/src/entities/order/ui/QuantityInput/QuantityInput.test.tsx new file mode 100644 index 0000000..ef52b2f --- /dev/null +++ b/src/entities/order/ui/QuantityInput/QuantityInput.test.tsx @@ -0,0 +1,51 @@ +import { act, render, screen } from '@testing-library/react'; +import userEvent from '@testing-library/user-event'; +import { describe, expect, it, vi } from 'vitest'; + +import QuantityInput from '.'; + +const initialProps = { + value: undefined, + onClickPlus: vi.fn(), + onClickMinus: vi.fn(), +}; + +describe('QuantityInput 컴포넌트 테스트', () => { + it('금액을 입력하면 해당 금액이 화면에 보인다.', async () => { + const user = userEvent.setup(); + + render(); + + const input = screen.getByRole('spinbutton'); + + await act(async () => { + await user.type(input, '1000'); + }); + + expect(input).toHaveValue(1000); + }); + + it('plus 버튼을 클릭하면 onClickPlus가 호출된다.', async () => { + const user = userEvent.setup(); + + render(); + + const plusButton = screen.getByTestId('plus-button'); + + await user.click(plusButton); + + expect(initialProps.onClickPlus).toHaveBeenCalledTimes(1); + }); + + it('minus 버튼을 클릭하면 onClickMinus가 호출된다.', async () => { + const user = userEvent.setup(); + + render(); + + const minusButton = screen.getByTestId('minus-button'); + + await user.click(minusButton); + + expect(initialProps.onClickMinus).toHaveBeenCalledTimes(1); + }); +}); diff --git a/src/entities/order/ui/QuantityInput/index.tsx b/src/entities/order/ui/QuantityInput/index.tsx index f18629e..8c71693 100644 --- a/src/entities/order/ui/QuantityInput/index.tsx +++ b/src/entities/order/ui/QuantityInput/index.tsx @@ -26,7 +26,7 @@ export default function QuantityInput({ className="-translate-y-1/2 absolute top-1/2 right-1 w-7 cursor-pointer rounded-sm p-1.5 px-2 hover:bg-gray-200 disabled:cursor-not-allowed" disabled={props.disabled} > - +
); diff --git a/src/entities/session/api/session.endpoint.ts b/src/entities/session/api/session.endpoint.ts index 588d310..a66dd83 100644 --- a/src/entities/session/api/session.endpoint.ts +++ b/src/entities/session/api/session.endpoint.ts @@ -1,3 +1,4 @@ +/* v8 ignore start */ import ApiClient from '~/shared/api/httpClient'; export default { @@ -5,3 +6,4 @@ export default { return ApiClient.get('api/logout'); }, }; +/* v8 ignore end */ diff --git a/src/entities/session/index.ts b/src/entities/session/index.ts index d7ecee3..c9593e7 100644 --- a/src/entities/session/index.ts +++ b/src/entities/session/index.ts @@ -1 +1,3 @@ +/* v8 ignore start */ export { default as api } from './api/session.endpoint'; +/* v8 ignore end */ diff --git a/src/entities/user/api/user.endpoint.ts b/src/entities/user/api/user.endpoint.ts index cdef0da..4dbc374 100644 --- a/src/entities/user/api/user.endpoint.ts +++ b/src/entities/user/api/user.endpoint.ts @@ -1,3 +1,4 @@ +/* v8 ignore start */ import ApiClient from '~/shared/api/httpClient'; import type { UserInfoResponse } from '../types/user.type'; @@ -6,3 +7,4 @@ export default { return ApiClient.get('api/userinfo'); }, }; +/* v8 ignore end */ diff --git a/src/entities/user/index.ts b/src/entities/user/index.ts index e6361f1..41fc40d 100644 --- a/src/entities/user/index.ts +++ b/src/entities/user/index.ts @@ -1 +1,3 @@ +/* v8 ignore start */ export { default as api } from './api/user.endpoint'; +/* v8 ignore end */ diff --git a/src/features/auth/api/auth.endpoints.ts b/src/features/auth/api/auth.endpoints.ts index f580db9..42a7ed8 100644 --- a/src/features/auth/api/auth.endpoints.ts +++ b/src/features/auth/api/auth.endpoints.ts @@ -1,3 +1,4 @@ +/* v8 ignore start */ import ApiClient from '~/shared/api/httpClient'; import type { TokenCheckResponse } from '../types/api'; @@ -6,3 +7,4 @@ export default { return ApiClient.get('api/tokencheck'); }, }; +/* v8 ignore end */ diff --git a/src/features/auth/index.ts b/src/features/auth/index.ts index 8bd6c35..d9a6f19 100644 --- a/src/features/auth/index.ts +++ b/src/features/auth/index.ts @@ -1,2 +1,4 @@ +/* v8 ignore start */ export { default as KakaoLoginButton } from './ui/KakaoLoginButton'; export { default as api } from './api/auth.endpoints'; +/* v8 ignore end */ diff --git a/src/features/chat/api/chat.endpoint.ts b/src/features/chat/api/chat.endpoint.ts index 7376bcd..ee4c385 100644 --- a/src/features/chat/api/chat.endpoint.ts +++ b/src/features/chat/api/chat.endpoint.ts @@ -1,3 +1,4 @@ +/* v8 ignore start */ import ky, { type KyResponse } from 'ky'; import type { Message } from '../types/chat.type'; @@ -11,3 +12,4 @@ export default { ); }, }; +/* v8 ignore end */ diff --git a/src/features/chat/index.ts b/src/features/chat/index.ts index 4464704..e05a904 100644 --- a/src/features/chat/index.ts +++ b/src/features/chat/index.ts @@ -1,2 +1,4 @@ +/* v8 ignore start */ export { default as AIChatBot } from './ui/AIChatBot'; export { default as api } from './api/chat.endpoint'; +/* v8 ignore end */ diff --git a/src/features/chat/ui/AIChatBot/AIChatBot.test.tsx b/src/features/chat/ui/AIChatBot/AIChatBot.test.tsx new file mode 100644 index 0000000..87c6b78 --- /dev/null +++ b/src/features/chat/ui/AIChatBot/AIChatBot.test.tsx @@ -0,0 +1,54 @@ +import { render, screen } from '@testing-library/react'; +import { describe, expect, it, vi } from 'vitest'; + +import userEvent from '@testing-library/user-event'; +import AIChatBot from '.'; + +vi.mock('~/shared/hooks/useScrollToBottom', () => ({ + default: () => ({ current: { scrollIntoView: vi.fn() } }), +})); + +describe('AIChatBot 컴포넌트 테스트', () => { + it('초기 상태에서 ChatButton이 보여진다.', () => { + render(); + + const chatButton = screen.getByTestId('chat-button'); + + expect(chatButton).toBeInTheDocument(); + }); + + it('사용자가 ChatButton을 클릭하면 ChatWindow가 보여진다.', async () => { + const user = userEvent.setup(); + render(); + + const chatButton = screen.getByTestId('chat-button'); + + expect(chatButton).toBeInTheDocument(); + + await user.click(chatButton); + + const chatWindow = screen.getByTestId('chat-window'); + + expect(chatWindow).toBeInTheDocument(); + }); + + it('사용자가 ChatButton을 클릭하면 ChatWindow가 보여졌다고 닫기 버튼을 누르면 ChatWindow가 사라지고 ChatButton이 보여진다.', async () => { + const user = userEvent.setup(); + render(); + + const chatButton = screen.getByTestId('chat-button'); + + expect(chatButton).toBeInTheDocument(); + + await user.click(chatButton); + + const chatWindow = screen.getByTestId('chat-window'); + const closeButton = screen.getByTestId('chat-window-close-button'); + + expect(chatWindow).toBeInTheDocument(); + + await user.click(closeButton); + + expect(chatWindow).not.toBeInTheDocument(); + }); +}); diff --git a/src/features/chat/ui/ChatButton/ChatButton.test.tsx b/src/features/chat/ui/ChatButton/ChatButton.test.tsx new file mode 100644 index 0000000..9fbf699 --- /dev/null +++ b/src/features/chat/ui/ChatButton/ChatButton.test.tsx @@ -0,0 +1,49 @@ +import { render, screen } from '@testing-library/react'; +import { describe, expect, it, vi } from 'vitest'; + +import { userEvent } from '@testing-library/user-event'; +import ChatButton from '.'; + +const props = { + isOpen: false, + handleClick: vi.fn(), +}; + +describe('ChatButton 컴포넌트 테스트', () => { + it('초기 상태에서 ChatButton이 보여진다.', () => { + render(); + + const button = screen.getByRole('button'); + + expect(button).toBeInTheDocument(); + }); + + it('button의 props로 isOpen이 true일 때 화면에 사라진다.', () => { + render(); + + const button = screen.getByRole('button'); + + expect(button).toHaveStyle({ opacity: 0, boxShadow: 'none' }); + }); + + it('button의 props로 isOpen이 false일 때 화면에 보인다.', () => { + render(); + + const button = screen.getByRole('button'); + + expect(button).toHaveStyle({ opacity: 1 }); + }); + + it('사용자가 button을 클릭하면 handleClick이 호출된다.', async () => { + const user = userEvent.setup(); + render(); + + const button = screen.getByRole('button'); + + expect(button).toBeInTheDocument(); + + await user.click(button); + + expect(props.handleClick).toHaveBeenCalledTimes(1); + }); +}); diff --git a/src/features/chat/ui/ChatButton/index.tsx b/src/features/chat/ui/ChatButton/index.tsx index ec9ab4e..9f29b6a 100644 --- a/src/features/chat/ui/ChatButton/index.tsx +++ b/src/features/chat/ui/ChatButton/index.tsx @@ -30,6 +30,7 @@ export default function ChatButton({ isOpen, handleClick }: ChatButtonProps) { animate={isOpen ? 'open' : 'closed'} variants={buttonVariant} exit={{ opacity: 0 }} + data-testid="chat-button" > diff --git a/src/features/chat/ui/ChatWindow/ChatWindow.test.tsx b/src/features/chat/ui/ChatWindow/ChatWindow.test.tsx new file mode 100644 index 0000000..e6357b4 --- /dev/null +++ b/src/features/chat/ui/ChatWindow/ChatWindow.test.tsx @@ -0,0 +1,126 @@ +import { render, screen } from '@testing-library/react'; +import { describe, expect, it, vi } from 'vitest'; + +import userEvent from '@testing-library/user-event'; +import ChatWindow from '.'; + +vi.mock('~/shared/hooks/useScrollToBottom', () => ({ + default: vi.fn(), +})); + +vi.mock('~/shared/hooks/useDimensions', () => ({ + default: () => ({ + height: 480, + width: 80, + }), +})); + +const props = { + children: null, + state: 'idle' as const, + inputValue: '', + messageList: [], + handleInputValueChange: vi.fn(), + handleSubmit: vi.fn(), + handleClose: vi.fn(), +}; + +describe('ChatWindow 컴포넌트 테스트', () => { + it('초기 상태에서 ChatWindow가 보여진다.', () => { + render(); + + const chatWindow = screen.getByTestId('chat-window'); + + expect(chatWindow).toBeInTheDocument(); + }); + + it('사용자는 idle 상태에서 메시지를 입력할 수 있다.', async () => { + const user = userEvent.setup(); + render(); + + const chatWindow = screen.getByTestId('chat-window'); + expect(chatWindow).toBeInTheDocument(); + + const input = screen.getByRole('textbox'); + expect(input).toBeInTheDocument(); + + await user.type(input, 'test'); + expect(props.handleInputValueChange).toHaveBeenCalledTimes(4); + }); + + it('사용자가 submit 버튼을 클릭하면 handleSubmit이 호출된다.', async () => { + const user = userEvent.setup(); + render(); + + const chatWindow = screen.getByTestId('chat-window'); + expect(chatWindow).toBeInTheDocument(); + + const submitButton = screen.getByRole('button', { name: '↑' }); + expect(submitButton).toBeInTheDocument(); + + await user.click(submitButton); + expect(props.handleSubmit).toHaveBeenCalledTimes(1); + }); + + it('processing 상태에서 input이 disabled된다.', () => { + render(); + + const chatWindow = screen.getByTestId('chat-window'); + expect(chatWindow).toBeInTheDocument(); + + const input = screen.getByRole('textbox'); + expect(input).toBeInTheDocument(); + + expect(input).toBeDisabled(); + }); + + it('complete 상태에서 input이 disabled된다.', () => { + render(); + + const chatWindow = screen.getByTestId('chat-window'); + expect(chatWindow).toBeInTheDocument(); + + const input = screen.getByRole('textbox'); + expect(input).toBeInTheDocument(); + + expect(input).toBeDisabled(); + }); + + it('processing 상태에서 submit 버튼이 disabled된다.', () => { + render(); + + const chatWindow = screen.getByTestId('chat-window'); + expect(chatWindow).toBeInTheDocument(); + + const submitButton = screen.getByRole('button', { name: '↑' }); + expect(submitButton).toBeInTheDocument(); + + expect(submitButton).toBeDisabled(); + }); + + it('complete 상태에서 submit 버튼이 disabled된다.', () => { + render(); + + const chatWindow = screen.getByTestId('chat-window'); + expect(chatWindow).toBeInTheDocument(); + + const submitButton = screen.getByRole('button', { name: '↑' }); + expect(submitButton).toBeInTheDocument(); + + expect(submitButton).toBeDisabled(); + }); + + it('사용자가 close 버튼을 누르면 handleClose 함수가 호출된다.', async () => { + const user = userEvent.setup(); + render(); + + const chatWindow = screen.getByTestId('chat-window'); + expect(chatWindow).toBeInTheDocument(); + + const closeButton = screen.getByRole('button', { name: '×' }); + expect(closeButton).toBeInTheDocument(); + + await user.click(closeButton); + expect(props.handleClose).toBeCalledTimes(1); + }); +}); diff --git a/src/features/chat/ui/ChatWindow/index.tsx b/src/features/chat/ui/ChatWindow/index.tsx index c3cf750..2164596 100644 --- a/src/features/chat/ui/ChatWindow/index.tsx +++ b/src/features/chat/ui/ChatWindow/index.tsx @@ -7,13 +7,11 @@ import { } from 'react'; import useDimensions from '~/shared/hooks/useDimensions'; -import useScrollToBottom from '~/shared/hooks/useScrollToBottom'; type ChatWindowProps = { children: ReactNode; state: 'idle' | 'processing' | 'complete'; inputValue: string; - messageList: unknown[]; handleInputValueChange: (e: ChangeEvent) => void; handleClose?: VoidFunction; handleSubmit: (e: FormEvent) => void; @@ -84,11 +82,10 @@ export default function ChatWindow({ handleInputValueChange: onInputValueChange, handleSubmit, handleClose, - messageList, }: ChatWindowProps) { const containerRef = useRef(null); - const messagesEndRef = useScrollToBottom([...messageList]); const { height } = useDimensions(containerRef); + const disabled = state === 'processing' || state === 'complete'; return ( ×
-
- {children} -
-
+
{children}
@@ -138,15 +134,13 @@ export default function ChatWindow({ type="text" className="flex-1 rounded-full border border-gray-300 px-4 py-2 focus:border-blue-500 focus:outline-none" placeholder="메시지를 입력하세요..." - disabled={state === 'processing'} - value={ - state === 'processing' ? 'AI가 답변 중입니다.' : inputValue - } + disabled={disabled} + value={disabled ? 'AI가 답변 중입니다.' : inputValue} onChange={onInputValueChange} /> ); + + const button = screen.getByRole('button'); + + expect(button).toHaveTextContent(text); + }); + + it('button을 클릭하면 props로 전달된 onClick이 호출된다.', async () => { + const spy = vi.fn(); + const user = userEvent.setup(); + + render(); + + const button = screen.getByRole('button'); + await user.click(button); + + expect(spy).toHaveBeenCalled(); + }); +}); diff --git a/src/shared/ui/ContainerTitle/ContainerTitle.test.tsx b/src/shared/ui/ContainerTitle/ContainerTitle.test.tsx new file mode 100644 index 0000000..7e0d8cc --- /dev/null +++ b/src/shared/ui/ContainerTitle/ContainerTitle.test.tsx @@ -0,0 +1,13 @@ +import { render, screen } from '@testing-library/react'; +import { describe, expect, it } from 'vitest'; + +import ContainerTitle from '.'; + +describe('ContainerTitle 컴포넌트 테스트', () => { + it('props로 전달된 string이 렌더링 된다.', () => { + const text = 'title'; + render({text}); + + expect(screen.getByText('title')).toBeInTheDocument(); + }); +}); diff --git a/src/shared/ui/Logo/Logo.test.tsx b/src/shared/ui/Logo/Logo.test.tsx new file mode 100644 index 0000000..51b4dd8 --- /dev/null +++ b/src/shared/ui/Logo/Logo.test.tsx @@ -0,0 +1,23 @@ +import { render, screen } from '@testing-library/react'; +import { describe, expect, it } from 'vitest'; + +import Logo from '.'; + +import CloudLogoBlack from '~/assets/images/cloud-black.webp'; +import CloudLogo from '~/assets/images/cloud.webp'; + +describe('Logo 컴포넌트 테스트', () => { + it('isBlack이 true일 때 블랙 로고가 렌더링 된다.', () => { + render(); + const logo = screen.getByRole('img'); + + expect(logo).toHaveAttribute('src', CloudLogoBlack); + }); + + it('isBlack이 false일 때 흰색 로고가 렌더링 된다.', () => { + render(); + const logo = screen.getByRole('img'); + + expect(logo).toHaveAttribute('src', CloudLogo); + }); +}); diff --git a/src/shared/ui/LogoWithTitle/LogoWithTitle.test.tsx b/src/shared/ui/LogoWithTitle/LogoWithTitle.test.tsx new file mode 100644 index 0000000..e56b2d8 --- /dev/null +++ b/src/shared/ui/LogoWithTitle/LogoWithTitle.test.tsx @@ -0,0 +1,14 @@ +import { render, screen } from '@testing-library/react'; +import { describe, expect, it } from 'vitest'; + +import LogoWithTitle from '.'; + +describe('LogoWithTitle 컴포넌트 테스트', () => { + it('props로 전달된 serviceName이 렌더링 된다.', () => { + render(); + + const component = screen.getByRole('heading'); + + expect(component).toHaveTextContent('서비스'); + }); +}); diff --git a/src/shared/ui/NumberInput/NumberInput.test.tsx b/src/shared/ui/NumberInput/NumberInput.test.tsx new file mode 100644 index 0000000..18863c0 --- /dev/null +++ b/src/shared/ui/NumberInput/NumberInput.test.tsx @@ -0,0 +1,31 @@ +import { render, screen } from '@testing-library/react'; +import userEvent from '@testing-library/user-event'; +import type { ChangeEvent } from 'react'; +import { describe, expect, it, vi } from 'vitest'; +import NumberInput from '.'; + +describe('NumberInput 컴포넌트 테스트', () => { + it('사용자가 숫자를 입력하면 onChange 이벤트가 발생한다.', async () => { + const onChange = (e: ChangeEvent) => {}; + const onChangeSpy = vi.fn(onChange); + render(); + const input = screen.getByRole('spinbutton'); + + const user = userEvent.setup(); + await user.type(input, '123'); + + expect(onChangeSpy).toHaveBeenCalled(); + expect(onChangeSpy.mock.calls[0][0].target.value).toBe('123'); + }); + + it('숫자가 아닌 문자를 입력하면 값이 변경되지 않는다', async () => { + const onChangeSpy = vi.fn(); + render(); + const input = screen.getByRole('spinbutton'); + + const user = userEvent.setup(); + await user.type(input, 'abc'); + + expect(input).toHaveValue(null); + }); +}); diff --git a/src/shared/ui/Switch/Switch.test.tsx b/src/shared/ui/Switch/Switch.test.tsx new file mode 100644 index 0000000..9a99766 --- /dev/null +++ b/src/shared/ui/Switch/Switch.test.tsx @@ -0,0 +1,56 @@ +import { render, screen } from '@testing-library/react'; +import userEvent from '@testing-library/user-event'; +import { beforeEach, describe, expect, it, vi } from 'vitest'; + +import Switch from '.'; + +const SWITCH_PROPS = { + value1: 'value1', + value2: 'value2', + text1: 'text1', + text2: 'text2', + selected: 'value1', + onChange: vi.fn(), +}; + +describe('Switch 컴포넌트 테스트', () => { + beforeEach(() => { + render(); + }); + + it('Switch에 props로 전달된 text1과 text2가 렌더링 된다.', async () => { + const text1 = screen.getByText(SWITCH_PROPS.text1); + const text2 = screen.getByText(SWITCH_PROPS.text2); + + expect(text1).toBeInTheDocument(); + expect(text2).toBeInTheDocument(); + }); + + it('Switch에 props로 전달된 value1과 value2가 렌더링 된다.', async () => { + const switchButton1 = screen.getByTestId('switch-button-1'); + const switchButton2 = screen.getByTestId('switch-button-2'); + + expect(switchButton1).toHaveValue(SWITCH_PROPS.value1); + expect(switchButton2).toHaveValue(SWITCH_PROPS.value2); + }); + + it('Switch를 누르면 해당 값이 선택된다.', async () => { + const user = userEvent.setup(); + + await user.click(screen.getByTestId('switch-button-1')); + expect(SWITCH_PROPS.onChange).toHaveBeenCalledWith(SWITCH_PROPS.value1); + + await user.click(screen.getByTestId('switch-button-2')); + expect(SWITCH_PROPS.onChange).toHaveBeenCalledWith(SWITCH_PROPS.value2); + }); + + it('같은 버튼을 연속으로 클릭할 수 있다.', async () => { + const user = userEvent.setup(); + + await user.click(screen.getByTestId('switch-button-1')); + expect(SWITCH_PROPS.onChange).toHaveBeenCalledWith(SWITCH_PROPS.value1); + + await user.click(screen.getByTestId('switch-button-1')); + expect(SWITCH_PROPS.onChange).toHaveBeenCalledWith(SWITCH_PROPS.value1); + }); +}); diff --git a/src/shared/ui/Switch/index.tsx b/src/shared/ui/Switch/index.tsx index 558f3e6..9b684bb 100644 --- a/src/shared/ui/Switch/index.tsx +++ b/src/shared/ui/Switch/index.tsx @@ -29,7 +29,10 @@ export default function Switch({ }; return ( -
+
({ onClick={handleSwitch} tabIndex={0} value={value1} + data-testid="switch-button-1" > {text1} @@ -51,6 +55,7 @@ export default function Switch({ onClick={handleSwitch} tabIndex={0} value={value2} + data-testid="switch-button-2" > {text2} diff --git a/src/shared/utils/utils.test.ts b/src/shared/utils/utils.test.ts new file mode 100644 index 0000000..ad21aa2 --- /dev/null +++ b/src/shared/utils/utils.test.ts @@ -0,0 +1,104 @@ +import type { KeyboardEvent } from 'react'; +import { describe, expect, it, vi } from 'vitest'; + +import { + filterNumber, + formatCurrencyKR, + isNegative, + isUndefined, + preventNonNumericInput, +} from '.'; + +describe('util 함수 테스트', () => { + describe('isUndefined 테스트', () => { + it('undefined가 전달되면 true를 반환한다.', () => { + const result = isUndefined(undefined); + + expect(result).toBe(true); + }); + + it('undefined를 제외한 값이 전달되면 false를 반환한다.', () => { + const result = isUndefined(123); + + expect(result).toBe(false); + }); + }); + + describe('isNegative 테스트', () => { + it('음수를 전달하면 true를 반환한다.', () => { + const result = isNegative(-1); + + expect(result).toBe(true); + }); + + it('양수를 전달하면 false를 반환한다.', () => { + const result = isNegative(1); + + expect(result).toBe(false); + }); + }); + + describe('formatCurrencyKR 테스트', () => { + it('123을 전달하면 문자열 123을 반환한다.', () => { + const result = formatCurrencyKR(123); + + expect(result).toBe('123'); + }); + + it('1234을 전달하면 문자열 1,234을 반환한다.', () => { + const result = formatCurrencyKR(1234); + + expect(result).toBe('1,234'); + }); + + it('1234.567을 전달하면 문자열 1,234.57을 반환한다.', () => { + const result = formatCurrencyKR(1234.567); + + expect(result).toBe('1,234.57'); + }); + + it('10000000을 전달하면 문자열 10,000,000을 반환한다.', () => { + const result = formatCurrencyKR(10000000); + + expect(result).toBe('10,000,000'); + }); + }); + + describe('filterNumber 테스트', () => { + it('숫자를 전달하면 true를 반환한다.', () => { + const result = filterNumber('123'); + + expect(result).toBe(true); + }); + + it('문자를 전달하면 false를 반환한다.', () => { + const result = filterNumber('qwer'); + + expect(result).toBe(false); + }); + }); + + describe('preventNonNumericInput 테스트', () => { + it('숫자 key가 아닌 key가 전달되면 preventDefault가 호출된다.', () => { + const event = { + key: 'a', + preventDefault: vi.fn(), + } as unknown as KeyboardEvent; + + preventNonNumericInput(event); + + expect(event.preventDefault).toHaveBeenCalled(); + }); + + it('숫자 key가 전달되면 preventDefault가 호출되지 않는다.', () => { + const event = { + key: '1', + preventDefault: vi.fn(), + } as unknown as KeyboardEvent; + + preventNonNumericInput(event); + + expect(event.preventDefault).not.toHaveBeenCalled(); + }); + }); +}); diff --git a/vitest.config.ts b/vitest.config.ts new file mode 100644 index 0000000..14dbb7e --- /dev/null +++ b/vitest.config.ts @@ -0,0 +1,19 @@ +import svgr from '@svgr/rollup'; +/// +import { defineConfig } from 'vite'; +import tsconfigPaths from 'vite-tsconfig-paths'; + +export default defineConfig({ + plugins: [svgr(), tsconfigPaths()], + test: { + environment: 'jsdom', + globals: true, + setupFiles: ['./setupTests.ts'], + coverage: { + provider: 'v8', + reporter: ['html', 'json', 'text', ['lcov', { projectRoot: './src' }]], + include: ['src/**/*'], + exclude: ['**/*.test.{ts,tsx}', '**/__tests__/**', '**/types/**'], + }, + }, +}); diff --git a/yarn.lock b/yarn.lock index 92278d1..2e02f88 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2,6 +2,11 @@ # yarn lockfile v1 +"@adobe/css-tools@^4.4.0": + version "4.4.3" + resolved "https://registry.yarnpkg.com/@adobe/css-tools/-/css-tools-4.4.3.tgz#beebbefb0264fdeb32d3052acae0e0d94315a9a2" + integrity sha512-VQKMkwriZbaOgVCby1UDY/LDk5fIjhQicCvVPFqfe+69fWaPWydbWJ3wRt59/YzIwda1I81loas3oCoHxnqvdA== + "@amcharts/amcharts5@^5.12.1": version "5.12.1" resolved "https://registry.yarnpkg.com/@amcharts/amcharts5/-/amcharts5-5.12.1.tgz#c416a98c7e5b19bcf1f7e91600ea29a2fe578325" @@ -33,7 +38,7 @@ svg-arc-to-cubic-bezier "^3.2.0" tslib "^2.2.0" -"@ampproject/remapping@^2.2.0": +"@ampproject/remapping@^2.2.0", "@ampproject/remapping@^2.3.0": version "2.3.0" resolved "https://registry.yarnpkg.com/@ampproject/remapping/-/remapping-2.3.0.tgz#ed441b6fa600072520ce18b43d2c8cc8caecc7f4" integrity sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw== @@ -41,7 +46,18 @@ "@jridgewell/gen-mapping" "^0.3.5" "@jridgewell/trace-mapping" "^0.3.24" -"@babel/code-frame@^7.0.0", "@babel/code-frame@^7.27.1": +"@asamuzakjp/css-color@^3.1.2": + version "3.2.0" + resolved "https://registry.yarnpkg.com/@asamuzakjp/css-color/-/css-color-3.2.0.tgz#cc42f5b85c593f79f1fa4f25d2b9b321e61d1794" + integrity sha512-K1A6z8tS3XsmCMM86xoWdn7Fkdn9m6RSVtocUrJYIwZnFVkng/PvkEoWtOWmP+Scc6saYWHWZYbndEEXxl24jw== + dependencies: + "@csstools/css-calc" "^2.1.3" + "@csstools/css-color-parser" "^3.0.9" + "@csstools/css-parser-algorithms" "^3.0.4" + "@csstools/css-tokenizer" "^3.0.3" + lru-cache "^10.4.3" + +"@babel/code-frame@^7.0.0", "@babel/code-frame@^7.10.4", "@babel/code-frame@^7.27.1": version "7.27.1" resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.27.1.tgz#200f715e66d52a23b221a9435534a91cc13ad5be" integrity sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg== @@ -240,6 +256,13 @@ dependencies: "@babel/types" "^7.27.1" +"@babel/parser@^7.25.4": + version "7.27.3" + resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.27.3.tgz#1b7533f0d908ad2ac545c4d05cbe2fb6dc8cfaaf" + integrity sha512-xyYxRj6+tLNDTWi0KCBcZ9V7yg3/lwL9DWh9Uwh/RIVlIfFidggcgxKX3GCXwCiswwcGRawBKbEg2LG/Y8eJhw== + dependencies: + "@babel/types" "^7.27.3" + "@babel/plugin-bugfix-firefox-class-in-computed-class-key@^7.27.1": version "7.27.1" resolved "https://registry.yarnpkg.com/@babel/plugin-bugfix-firefox-class-in-computed-class-key/-/plugin-bugfix-firefox-class-in-computed-class-key-7.27.1.tgz#61dd8a8e61f7eb568268d1b5f129da3eee364bf9" @@ -872,6 +895,11 @@ "@babel/plugin-transform-modules-commonjs" "^7.27.1" "@babel/plugin-transform-typescript" "^7.27.1" +"@babel/runtime@^7.12.5": + version "7.27.3" + resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.27.3.tgz#10491113799fb8d77e1d9273384d5d68deeea8f6" + integrity sha512-7EYtGezsdiDMyY80+65EzwiGmcJqpmcZCojSXaRgdrBaGtWTgDZKq69cPIVped6MkIM78cTQ2GOiEYjwOlG4xw== + "@babel/template@^7.27.1": version "7.27.2" resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.27.2.tgz#fa78ceed3c4e7b63ebf6cb39e5852fca45f6809d" @@ -902,6 +930,19 @@ "@babel/helper-string-parser" "^7.27.1" "@babel/helper-validator-identifier" "^7.27.1" +"@babel/types@^7.25.4", "@babel/types@^7.27.3": + version "7.27.3" + resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.27.3.tgz#c0257bedf33aad6aad1f406d35c44758321eb3ec" + integrity sha512-Y1GkI4ktrtvmawoSq+4FCVHNryea6uR+qUQy0AGxLSsjCX0nVmkYQMBLHDkXZuo5hGx7eYdnIaslsdBFm7zbUw== + dependencies: + "@babel/helper-string-parser" "^7.27.1" + "@babel/helper-validator-identifier" "^7.27.1" + +"@bcoe/v8-coverage@^1.0.2": + version "1.0.2" + resolved "https://registry.yarnpkg.com/@bcoe/v8-coverage/-/v8-coverage-1.0.2.tgz#bbe12dca5b4ef983a0d0af4b07b9bc90ea0ababa" + integrity sha512-6zABk/ECA/QYSCQ1NGiVwwbQerUCZ+TQbp64Q3AgmfNvurHH0j8TtXa1qbShXA6qqkpAj4V5W8pP6mLe1mcMqA== + "@biomejs/biome@1.9.4": version "1.9.4" resolved "https://registry.yarnpkg.com/@biomejs/biome/-/biome-1.9.4.tgz#89766281cbc3a0aae865a7ff13d6aaffea2842bf" @@ -978,6 +1019,34 @@ "@types/tough-cookie" "^4.0.5" tough-cookie "^4.1.4" +"@csstools/color-helpers@^5.0.2": + version "5.0.2" + resolved "https://registry.yarnpkg.com/@csstools/color-helpers/-/color-helpers-5.0.2.tgz#82592c9a7c2b83c293d9161894e2a6471feb97b8" + integrity sha512-JqWH1vsgdGcw2RR6VliXXdA0/59LttzlU8UlRT/iUUsEeWfYq8I+K0yhihEUTTHLRm1EXvpsCx3083EU15ecsA== + +"@csstools/css-calc@^2.1.3", "@csstools/css-calc@^2.1.4": + version "2.1.4" + resolved "https://registry.yarnpkg.com/@csstools/css-calc/-/css-calc-2.1.4.tgz#8473f63e2fcd6e459838dd412401d5948f224c65" + integrity sha512-3N8oaj+0juUw/1H3YwmDDJXCgTB1gKU6Hc/bB502u9zR0q2vd786XJH9QfrKIEgFlZmhZiq6epXl4rHqhzsIgQ== + +"@csstools/css-color-parser@^3.0.9": + version "3.0.10" + resolved "https://registry.yarnpkg.com/@csstools/css-color-parser/-/css-color-parser-3.0.10.tgz#79fc68864dd43c3b6782d2b3828bc0fa9d085c10" + integrity sha512-TiJ5Ajr6WRd1r8HSiwJvZBiJOqtH86aHpUjq5aEKWHiII2Qfjqd/HCWKPOW8EP4vcspXbHnXrwIDlu5savQipg== + dependencies: + "@csstools/color-helpers" "^5.0.2" + "@csstools/css-calc" "^2.1.4" + +"@csstools/css-parser-algorithms@^3.0.4": + version "3.0.5" + resolved "https://registry.yarnpkg.com/@csstools/css-parser-algorithms/-/css-parser-algorithms-3.0.5.tgz#5755370a9a29abaec5515b43c8b3f2cf9c2e3076" + integrity sha512-DaDeUkXZKjdGhgYaHNJTV9pV7Y9B3b644jCLs9Upc3VeNGg6LWARAT6O+Q+/COo+2gg/bM5rhpMAtf70WqfBdQ== + +"@csstools/css-tokenizer@^3.0.3": + version "3.0.4" + resolved "https://registry.yarnpkg.com/@csstools/css-tokenizer/-/css-tokenizer-3.0.4.tgz#333fedabc3fd1a8e5d0100013731cf19e6a8c5d3" + integrity sha512-Vd/9EVDiu6PPJt9yAh6roZP6El1xHrdvIVGjyBsHR0RYwNHgL7FJPyIIW4fANJNG6FtyZfvlRPpFI4ZM/lubvw== + "@emnapi/core@^1.4.0", "@emnapi/core@^1.4.3": version "1.4.3" resolved "https://registry.yarnpkg.com/@emnapi/core/-/core-1.4.3.tgz#9ac52d2d5aea958f67e52c40a065f51de59b77d6" @@ -1207,6 +1276,11 @@ wrap-ansi "^8.1.0" wrap-ansi-cjs "npm:wrap-ansi@^7.0.0" +"@istanbuljs/schema@^0.1.2": + version "0.1.3" + resolved "https://registry.yarnpkg.com/@istanbuljs/schema/-/schema-0.1.3.tgz#e45e384e4b8ec16bce2fd903af78450f6bf7ec98" + integrity sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA== + "@jridgewell/gen-mapping@^0.3.5": version "0.3.8" resolved "https://registry.yarnpkg.com/@jridgewell/gen-mapping/-/gen-mapping-0.3.8.tgz#4f0e06362e01362f823d348f1872b08f666d8142" @@ -1226,12 +1300,12 @@ resolved "https://registry.yarnpkg.com/@jridgewell/set-array/-/set-array-1.2.1.tgz#558fb6472ed16a4c850b889530e6b36438c49280" integrity sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A== -"@jridgewell/sourcemap-codec@^1.4.10", "@jridgewell/sourcemap-codec@^1.4.14": +"@jridgewell/sourcemap-codec@^1.4.10", "@jridgewell/sourcemap-codec@^1.4.14", "@jridgewell/sourcemap-codec@^1.5.0": version "1.5.0" resolved "https://registry.yarnpkg.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.0.tgz#3188bcb273a414b0d215fd22a58540b989b9409a" integrity sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ== -"@jridgewell/trace-mapping@^0.3.24", "@jridgewell/trace-mapping@^0.3.25": +"@jridgewell/trace-mapping@^0.3.23", "@jridgewell/trace-mapping@^0.3.24", "@jridgewell/trace-mapping@^0.3.25": version "0.3.25" resolved "https://registry.yarnpkg.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz#15f190e98895f3fc23276ee14bc76b675c2e50f0" integrity sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ== @@ -1717,6 +1791,45 @@ "@tailwindcss/oxide" "4.1.5" tailwindcss "4.1.5" +"@testing-library/dom@^10.4.0": + version "10.4.0" + resolved "https://registry.yarnpkg.com/@testing-library/dom/-/dom-10.4.0.tgz#82a9d9462f11d240ecadbf406607c6ceeeff43a8" + integrity sha512-pemlzrSESWbdAloYml3bAJMEfNh1Z7EduzqPKprCH5S341frlpYnUEW0H72dLxa6IsYr+mPno20GiSm+h9dEdQ== + 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" + +"@testing-library/jest-dom@^6.6.3": + version "6.6.3" + resolved "https://registry.yarnpkg.com/@testing-library/jest-dom/-/jest-dom-6.6.3.tgz#26ba906cf928c0f8172e182c6fe214eb4f9f2bd2" + integrity sha512-IteBhl4XqYNkM54f4ejhLRJiZNqcSCoXUOG2CPK7qbD322KjQozM4kHQOfkG2oln9b9HTYqs+Sae8vBATubxxA== + dependencies: + "@adobe/css-tools" "^4.4.0" + aria-query "^5.0.0" + chalk "^3.0.0" + css.escape "^1.5.1" + dom-accessibility-api "^0.6.3" + lodash "^4.17.21" + redent "^3.0.0" + +"@testing-library/react@^16.3.0": + version "16.3.0" + resolved "https://registry.yarnpkg.com/@testing-library/react/-/react-16.3.0.tgz#3a85bb9bdebf180cd76dba16454e242564d598a6" + integrity sha512-kFSyxiEDwv1WLl2fgsq6pPBbw5aWKrsY2/noi1Id0TK0UParSF62oFQFGHXIyaG4pp2tEub/Zlel+fjjZILDsw== + dependencies: + "@babel/runtime" "^7.12.5" + +"@testing-library/user-event@^14.6.1": + version "14.6.1" + resolved "https://registry.yarnpkg.com/@testing-library/user-event/-/user-event-14.6.1.tgz#13e09a32d7a8b7060fe38304788ebf4197cd2149" + integrity sha512-vq7fv0rnt+QTXgPxr5Hjc210p6YKq2kmdziLgnsZGgLJ9e6VAShx1pACLuRjd/AS/sr7phAR58OIIpf0LlmQNw== + "@trysound/sax@0.2.0": version "0.2.0" resolved "https://registry.yarnpkg.com/@trysound/sax/-/sax-0.2.0.tgz#cccaab758af56761eb7bf37af6f03f326dd798ad" @@ -1729,6 +1842,11 @@ dependencies: tslib "^2.4.0" +"@types/aria-query@^5.0.1": + version "5.0.4" + resolved "https://registry.yarnpkg.com/@types/aria-query/-/aria-query-5.0.4.tgz#1a31c3d378850d2778dabb6374d036dcba4ba708" + integrity sha512-rfT93uj5s0PRL7EzccGMs3brplhcrghnDoV26NqKhCAS1hVo+WdNsPvE/yb6ilfr5hi2MEk6d5EWJTKdxg8jVw== + "@types/cookie@^0.6.0": version "0.6.0" resolved "https://registry.yarnpkg.com/@types/cookie/-/cookie-0.6.0.tgz#eac397f28bf1d6ae0ae081363eca2f425bedf0d5" @@ -2017,6 +2135,83 @@ resolved "https://registry.yarnpkg.com/@types/tough-cookie/-/tough-cookie-4.0.5.tgz#cb6e2a691b70cb177c6e3ae9c1d2e8b2ea8cd304" integrity sha512-/Ad8+nIOV7Rl++6f1BdKxFSMgmoqEoYbHRpPcx3JEfv8VRsQe9Z4mCXeJBzxs7mbHY/XOZZuXlRNfhpVPbs6ZA== +"@vitest/coverage-v8@^3.1.4": + version "3.1.4" + resolved "https://registry.yarnpkg.com/@vitest/coverage-v8/-/coverage-v8-3.1.4.tgz#faffd0d22795938b69aa4fedc78622bce299ec26" + integrity sha512-G4p6OtioySL+hPV7Y6JHlhpsODbJzt1ndwHAFkyk6vVjpK03PFsKnauZIzcd0PrK4zAbc5lc+jeZ+eNGiMA+iw== + dependencies: + "@ampproject/remapping" "^2.3.0" + "@bcoe/v8-coverage" "^1.0.2" + debug "^4.4.0" + 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" + +"@vitest/expect@3.1.4": + version "3.1.4" + resolved "https://registry.yarnpkg.com/@vitest/expect/-/expect-3.1.4.tgz#837651a71682e3611c3df7b58b157ba485bd8029" + integrity sha512-xkD/ljeliyaClDYqHPNCiJ0plY5YIcM0OlRiZizLhlPmpXWpxnGMyTZXOHFhFeG7w9P5PBeL4IdtJ/HeQwTbQA== + dependencies: + "@vitest/spy" "3.1.4" + "@vitest/utils" "3.1.4" + chai "^5.2.0" + tinyrainbow "^2.0.0" + +"@vitest/mocker@3.1.4": + version "3.1.4" + resolved "https://registry.yarnpkg.com/@vitest/mocker/-/mocker-3.1.4.tgz#73441022b86c7299bfbd11a9fb2e99a7ddc2bb0e" + integrity sha512-8IJ3CvwtSw/EFXqWFL8aCMu+YyYXG2WUSrQbViOZkWTKTVicVwZ/YiEZDSqD00kX+v/+W+OnxhNWoeVKorHygA== + dependencies: + "@vitest/spy" "3.1.4" + estree-walker "^3.0.3" + magic-string "^0.30.17" + +"@vitest/pretty-format@3.1.4", "@vitest/pretty-format@^3.1.4": + version "3.1.4" + resolved "https://registry.yarnpkg.com/@vitest/pretty-format/-/pretty-format-3.1.4.tgz#da3e98c250cde3ce39fe8e709339814607b185e8" + integrity sha512-cqv9H9GvAEoTaoq+cYqUTCGscUjKqlJZC7PRwY5FMySVj5J+xOm1KQcCiYHJOEzOKRUhLH4R2pTwvFlWCEScsg== + dependencies: + tinyrainbow "^2.0.0" + +"@vitest/runner@3.1.4": + version "3.1.4" + resolved "https://registry.yarnpkg.com/@vitest/runner/-/runner-3.1.4.tgz#19fa16eb397f5325b99baca48c2bca6cadd098fa" + integrity sha512-djTeF1/vt985I/wpKVFBMWUlk/I7mb5hmD5oP8K9ACRmVXgKTae3TUOtXAEBfslNKPzUQvnKhNd34nnRSYgLNQ== + dependencies: + "@vitest/utils" "3.1.4" + pathe "^2.0.3" + +"@vitest/snapshot@3.1.4": + version "3.1.4" + resolved "https://registry.yarnpkg.com/@vitest/snapshot/-/snapshot-3.1.4.tgz#7897d4960a3cf617fb0f17e182cc15c7e3e4ed3f" + integrity sha512-JPHf68DvuO7vilmvwdPr9TS0SuuIzHvxeaCkxYcCD4jTk67XwL45ZhEHFKIuCm8CYstgI6LZ4XbwD6ANrwMpFg== + dependencies: + "@vitest/pretty-format" "3.1.4" + magic-string "^0.30.17" + pathe "^2.0.3" + +"@vitest/spy@3.1.4": + version "3.1.4" + resolved "https://registry.yarnpkg.com/@vitest/spy/-/spy-3.1.4.tgz#94bb566da7ef6deb7c4e1fd79b78f19aa5465b9f" + integrity sha512-Xg1bXhu+vtPXIodYN369M86K8shGLouNjoVI78g8iAq2rFoHFdajNvJJ5A/9bPMFcfQqdaCpOgWKEoMQg/s0Yg== + dependencies: + tinyspy "^3.0.2" + +"@vitest/utils@3.1.4": + version "3.1.4" + resolved "https://registry.yarnpkg.com/@vitest/utils/-/utils-3.1.4.tgz#f9f20d92f1384a9d66548c480885390760047b5e" + integrity sha512-yriMuO1cfFhmiGc8ataN51+9ooHRuURdfAZfwFd3usWynjzpLslZdYnRegTv32qdgtJTsj15FoeZe2g15fY1gg== + dependencies: + "@vitest/pretty-format" "3.1.4" + loupe "^3.1.3" + tinyrainbow "^2.0.0" + "@xstate/react@^5.0.4": version "5.0.4" resolved "https://registry.yarnpkg.com/@xstate/react/-/react-5.0.4.tgz#e04b5d558fd54e3d1704efb13b3ac22636d95e8e" @@ -2033,6 +2228,11 @@ accepts@~1.3.8: mime-types "~2.1.34" negotiator "0.6.3" +agent-base@^7.1.0, agent-base@^7.1.2: + version "7.1.3" + resolved "https://registry.yarnpkg.com/agent-base/-/agent-base-7.1.3.tgz#29435eb821bc4194633a5b89e5bc4703bafc25a1" + integrity sha512-jRR5wdylq8CkOe6hei19GGZnxM6rBGwFl3Bg0YItGDimvjGtAvdZk4Pu6Cl4u4Igsws4a1fd1Vq3ezrhn4KmFw== + ansi-escapes@^4.3.2: version "4.3.2" resolved "https://registry.yarnpkg.com/ansi-escapes/-/ansi-escapes-4.3.2.tgz#6b2291d1db7d98b6521d5f1efa42d0f3a9feb65e" @@ -2057,13 +2257,18 @@ ansi-regex@^6.0.1: resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-6.1.0.tgz#95ec409c69619d6cb1b8b34f14b660ef28ebd654" integrity sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA== -ansi-styles@^4.0.0: +ansi-styles@^4.0.0, ansi-styles@^4.1.0: version "4.3.0" resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-4.3.0.tgz#edd803628ae71c04c85ae7a0906edad34b648937" integrity sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg== dependencies: color-convert "^2.0.1" +ansi-styles@^5.0.0: + version "5.2.0" + resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-5.2.0.tgz#07449690ad45777d1924ac2abb2fc8895dba836b" + integrity sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA== + ansi-styles@^6.0.0, ansi-styles@^6.1.0, ansi-styles@^6.2.1: version "6.2.1" resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-6.2.1.tgz#0e62320cf99c21afff3b3012192546aacbfb05c5" @@ -2079,11 +2284,28 @@ argparse@^2.0.1: resolved "https://registry.yarnpkg.com/argparse/-/argparse-2.0.1.tgz#246f50f3ca78a3240f6c997e8a9bd1eac49e4b38" integrity sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q== +aria-query@5.3.0: + version "5.3.0" + resolved "https://registry.yarnpkg.com/aria-query/-/aria-query-5.3.0.tgz#650c569e41ad90b51b3d7df5e5eed1c7549c103e" + integrity sha512-b0P0sZPKtyu8HkeRAfCq0IfURZK+SuwMjY1UXGBU27wpAiTwQAIlq56IbIO+ytk/JjS1fMR14ee5WBBfKi5J6A== + dependencies: + dequal "^2.0.3" + +aria-query@^5.0.0: + version "5.3.2" + resolved "https://registry.yarnpkg.com/aria-query/-/aria-query-5.3.2.tgz#93f81a43480e33a338f19163a3d10a50c01dcd59" + integrity sha512-COROpnaoap1E2F000S62r6A60uHZnmlvomhfyT2DlTcrY1OrBKn2UhH7qn5wTC9zMvD0AY7csdPSNwKP+7WiQw== + array-flatten@1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/array-flatten/-/array-flatten-1.1.1.tgz#9a5f699051b1e7073328f2a008968b64ea2955d2" integrity sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg== +assertion-error@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/assertion-error/-/assertion-error-2.0.1.tgz#f641a196b335690b1070bf00b6e7593fec190bf7" + integrity sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA== + babel-dead-code-elimination@^1.0.6: version "1.0.10" resolved "https://registry.yarnpkg.com/babel-dead-code-elimination/-/babel-dead-code-elimination-1.0.10.tgz#e230562b57bf72ff3de4639ac763ba54f15d37b0" @@ -2250,11 +2472,43 @@ caniuse-lite@^1.0.30001716: resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001717.tgz#5d9fec5ce09796a1893013825510678928aca129" integrity sha512-auPpttCq6BDEG8ZAuHJIplGw6GODhjw+/11e7IjpnYCxZcW/ONgPs0KVBJ0d1bY3e2+7PRe5RCLyP+PfwVgkYw== +chai@^5.2.0: + version "5.2.0" + resolved "https://registry.yarnpkg.com/chai/-/chai-5.2.0.tgz#1358ee106763624114addf84ab02697e411c9c05" + integrity sha512-mCuXncKXk5iCLhfhwTc0izo0gtEmpz5CtG2y8GiOINBlMVS6v8TMRc5TaLWKS6692m9+dVVfzgeVxR5UxWHTYw== + dependencies: + assertion-error "^2.0.1" + check-error "^2.1.1" + deep-eql "^5.0.1" + loupe "^3.1.0" + pathval "^2.0.0" + +chalk@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/chalk/-/chalk-3.0.0.tgz#3f73c2bf526591f574cc492c51e2456349f844e4" + integrity sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg== + dependencies: + ansi-styles "^4.1.0" + supports-color "^7.1.0" + +chalk@^4.1.0: + version "4.1.2" + resolved "https://registry.yarnpkg.com/chalk/-/chalk-4.1.2.tgz#aac4e2b7734a740867aeb16bf02aad556a1e7a01" + integrity sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA== + dependencies: + ansi-styles "^4.1.0" + supports-color "^7.1.0" + chalk@^5.4.1: version "5.4.1" resolved "https://registry.yarnpkg.com/chalk/-/chalk-5.4.1.tgz#1b48bf0963ec158dce2aacf69c093ae2dd2092d8" integrity sha512-zgVZuo2WcZgfUEmsn6eO3kINexW8RAE4maiQ8QNs8CtpPCSyMiYsULR3HQYkm3w8FIA3SberyMJMSldGsW+U3w== +check-error@^2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/check-error/-/check-error-2.1.1.tgz#87eb876ae71ee388fa0471fe423f494be1d96ccc" + integrity sha512-OAlb+T7V4Op9OwdkjmguYRqncdlx5JiofwOAUkmTF+jNdHwzTaTs4sRAGpzLF3oOz5xAyDGrPgeIDFQmDOTiJw== + chokidar@^4.0.0: version "4.0.3" resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-4.0.3.tgz#7be37a4c03c9aee1ecfe862a4a23b2c70c205d30" @@ -2448,6 +2702,11 @@ css-what@^6.1.0: resolved "https://registry.yarnpkg.com/css-what/-/css-what-6.1.0.tgz#fb5effcf76f1ddea2c81bdfaa4de44e79bac70f4" integrity sha512-HTUrgRJ7r4dsZKU6GjmpfRK1O76h97Z8MfS1G0FozR+oF2kG6Vfe8JE6zwrkbxigziPHinCJ+gCPjA9EaBDtRw== +css.escape@^1.5.1: + version "1.5.1" + resolved "https://registry.yarnpkg.com/css.escape/-/css.escape-1.5.1.tgz#42e27d4fa04ae32f931a4b4d4191fa9cddee97cb" + integrity sha512-YUifsXXuknHlUsmlgyY0PKzgPOr7/FjCePfHNt0jxm83wHZi44VDMQ7/fGNkjY3/jV1MC+1CmZbaHzugyeRtpg== + csso@^5.0.5: version "5.0.5" resolved "https://registry.yarnpkg.com/csso/-/csso-5.0.5.tgz#f9b7fe6cc6ac0b7d90781bb16d5e9874303e2ca6" @@ -2455,6 +2714,14 @@ csso@^5.0.5: dependencies: css-tree "~2.2.0" +cssstyle@^4.2.1: + version "4.3.1" + resolved "https://registry.yarnpkg.com/cssstyle/-/cssstyle-4.3.1.tgz#68a3c9f5a70aa97d5a6ebecc9805e511fc022eb8" + integrity sha512-ZgW+Jgdd7i52AaLYCriF8Mxqft0gD/R9i9wi6RWBhs1pqdPEzPjym7rvRKi397WmQFf3SlyUsszhw+VVCbx79Q== + dependencies: + "@asamuzakjp/css-color" "^3.1.2" + rrweb-cssom "^0.8.0" + csstype@^3.0.2: version "3.1.3" resolved "https://registry.yarnpkg.com/csstype/-/csstype-3.1.3.tgz#d80ff294d114fb0e6ac500fbf85b60137d7eff81" @@ -2771,6 +3038,14 @@ d3@^7.0.0: d3-transition "3" d3-zoom "3" +data-urls@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/data-urls/-/data-urls-5.0.0.tgz#2f76906bce1824429ffecb6920f45a0b30f00dde" + integrity sha512-ZYP5VBHshaDAiVZxjbRVcFJpc+4xGgT0bK3vzy1HLN8jTO975HEbuYzZJcHoQEY5K1a0z8YayJkyVETa08eNTg== + dependencies: + whatwg-mimetype "^4.0.0" + whatwg-url "^14.0.0" + debug@2.6.9: version "2.6.9" resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f" @@ -2778,6 +3053,13 @@ debug@2.6.9: dependencies: ms "2.0.0" +debug@4, debug@^4.3.4: + version "4.4.1" + resolved "https://registry.yarnpkg.com/debug/-/debug-4.4.1.tgz#e5a8bc6cbc4c6cd3e64308b0693a3d4fa550189b" + integrity sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ== + dependencies: + ms "^2.1.3" + debug@^4.1.0, debug@^4.1.1, debug@^4.3.1, debug@^4.4.0: version "4.4.0" resolved "https://registry.yarnpkg.com/debug/-/debug-4.4.0.tgz#2b3f2aea2ffeb776477460267377dc8710faba8a" @@ -2785,11 +3067,21 @@ debug@^4.1.0, debug@^4.1.1, debug@^4.3.1, debug@^4.4.0: dependencies: ms "^2.1.3" +decimal.js@^10.5.0: + version "10.5.0" + resolved "https://registry.yarnpkg.com/decimal.js/-/decimal.js-10.5.0.tgz#0f371c7cf6c4898ce0afb09836db73cd82010f22" + integrity sha512-8vDa8Qxvr/+d94hSh5P3IJwI5t8/c0KsMp+g8bNw9cY2icONa5aPfvKeieW1WlG0WQYwwhJ7mjui2xtiePQSXw== + dedent@^1.5.3: version "1.6.0" resolved "https://registry.yarnpkg.com/dedent/-/dedent-1.6.0.tgz#79d52d6389b1ffa67d2bcef59ba51847a9d503b2" integrity sha512-F1Z+5UCFpmQUzJa11agbyPVMbpgT/qA3/SKyJ1jyBgm7dUcUEa8v9JwDkerSQXfakBwFljIxhOJqGkjUwZ9FSA== +deep-eql@^5.0.1: + version "5.0.2" + resolved "https://registry.yarnpkg.com/deep-eql/-/deep-eql-5.0.2.tgz#4b756d8d770a9257300825d52a2c2cff99c3a341" + integrity sha512-h5k/5U50IJJFpzfL6nO9jaaumfjO/f2NjK/oYB2Djzm4p9L+3T9qWpZqZ2hAbLPuuYq9wrU08WQyBTL5GbPk5Q== + deep-equal@^1.0.0: version "1.1.2" resolved "https://registry.yarnpkg.com/deep-equal/-/deep-equal-1.1.2.tgz#78a561b7830eef3134c7f6f3a3d6af272a678761" @@ -2837,6 +3129,11 @@ depd@2.0.0, depd@~2.0.0: resolved "https://registry.yarnpkg.com/depd/-/depd-2.0.0.tgz#b696163cc757560d09cf22cc8fad1571b79e76df" integrity sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw== +dequal@^2.0.3: + version "2.0.3" + resolved "https://registry.yarnpkg.com/dequal/-/dequal-2.0.3.tgz#2644214f1997d39ed0ee0ece72335490a7ac67be" + integrity sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA== + destroy@1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/destroy/-/destroy-1.2.0.tgz#4803735509ad8be552934c67df614f94e66fa015" @@ -2852,6 +3149,16 @@ dfa@^1.2.0: resolved "https://registry.yarnpkg.com/dfa/-/dfa-1.2.0.tgz#96ac3204e2d29c49ea5b57af8d92c2ae12790657" integrity sha512-ED3jP8saaweFTjeGX8HQPjeC1YYyZs98jGNZx6IiBvxW7JG5v492kamAQB3m2wop07CvU/RQmzcKr6bgcC5D/Q== +dom-accessibility-api@^0.5.9: + version "0.5.16" + resolved "https://registry.yarnpkg.com/dom-accessibility-api/-/dom-accessibility-api-0.5.16.tgz#5a7429e6066eb3664d911e33fb0e45de8eb08453" + integrity sha512-X7BJ2yElsnOJ30pZF4uIIDfBEVgF4XEBxL9Bxhy6dnrm5hkzqmsWHGTiHqRiITNhMyFLyAiWndIJP7Z1NTteDg== + +dom-accessibility-api@^0.6.3: + version "0.6.3" + resolved "https://registry.yarnpkg.com/dom-accessibility-api/-/dom-accessibility-api-0.6.3.tgz#993e925cc1d73f2c662e7d75dd5a5445259a8fd8" + integrity sha512-7ZgogeTnjuHbo+ct10G9Ffp0mif17idi0IyWNVA/wcwcm7NPOD/WEHVP3n7n3MhXqxoIYm8d6MuZohYWIZ4T3w== + dom-serializer@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/dom-serializer/-/dom-serializer-2.0.0.tgz#e41b802e1eedf9f6cae183ce5e622d789d7d8e53" @@ -2952,6 +3259,11 @@ entities@^4.2.0, entities@^4.4.0: resolved "https://registry.yarnpkg.com/entities/-/entities-4.5.0.tgz#5d268ea5e7113ec74c4d033b79ea5a35a488fb48" integrity sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw== +entities@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/entities/-/entities-6.0.0.tgz#09c9e29cb79b0a6459a9b9db9efb418ac5bb8e51" + integrity sha512-aKstq2TDOndCn4diEyp9Uq/Flu2i1GlLkc6XIDQSDMuaFE3OPW5OphLCyQ5SpSJZTb4reN+kTcYru5yIfXoRPw== + environment@^1.0.0: version "1.1.0" resolved "https://registry.yarnpkg.com/environment/-/environment-1.1.0.tgz#8e86c66b180f363c7ab311787e0259665f45a9f1" @@ -2979,7 +3291,7 @@ es-errors@^1.3.0: resolved "https://registry.yarnpkg.com/es-errors/-/es-errors-1.3.0.tgz#05f75a25dab98e4fb1dcd5e1472c0546d5057c8f" integrity sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw== -es-module-lexer@^1.3.1, es-module-lexer@^1.5.4: +es-module-lexer@^1.3.1, es-module-lexer@^1.5.4, es-module-lexer@^1.7.0: version "1.7.0" resolved "https://registry.yarnpkg.com/es-module-lexer/-/es-module-lexer-1.7.0.tgz#9159601561880a85f2734560a9099b2c31e5372a" integrity sha512-jEQoCwk8hyb2AZziIOLhDqpm5+2ww5uIE6lkO/6jcOCusfk6LhMHpXXfBLXTZ7Ydyt0j4VoUQv6uGNYbdW+kBA== @@ -3037,6 +3349,13 @@ estree-walker@^2.0.2: resolved "https://registry.yarnpkg.com/estree-walker/-/estree-walker-2.0.2.tgz#52f010178c2a4c117a7757cfe942adb7d2da4cac" integrity sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w== +estree-walker@^3.0.3: + version "3.0.3" + resolved "https://registry.yarnpkg.com/estree-walker/-/estree-walker-3.0.3.tgz#67c3e549ec402a487b4fc193d1953a524752340d" + integrity sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g== + dependencies: + "@types/estree" "^1.0.0" + esutils@^2.0.2: version "2.0.3" resolved "https://registry.yarnpkg.com/esutils/-/esutils-2.0.3.tgz#74d2eb4de0b8da1293711910d50775b9b710ef64" @@ -3072,6 +3391,11 @@ exit-hook@2.2.1: resolved "https://registry.yarnpkg.com/exit-hook/-/exit-hook-2.2.1.tgz#007b2d92c6428eda2b76e7016a34351586934593" integrity sha512-eNTPlAD67BmP31LDINZ3U7HSF8l57TxOY2PmBJ1shpCvpnxBF93mWCE8YHBnXs8qiUZJc9WDcWIeC3a2HIAMfw== +expect-type@^1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/expect-type/-/expect-type-1.2.1.tgz#af76d8b357cf5fa76c41c09dafb79c549e75f71f" + integrity sha512-/kP8CAwxzLVEeFrMm4kMmy4CCDlpipyA7MYLVrdJIkV0fYF0UaigQHRsxHiuY/GEea+bh4KSv3TIlgr+2UL6bw== + express@^4.19.2: version "4.21.2" resolved "https://registry.yarnpkg.com/express/-/express-4.21.2.tgz#cf250e48362174ead6cea4a566abef0162c1ec32" @@ -3239,7 +3563,7 @@ get-stream@^8.0.1: resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-8.0.1.tgz#def9dfd71742cd7754a7761ed43749a27d02eca2" integrity sha512-VaUJspBffn/LMCJVoMvSAdmscJyS1auj5Zulnn5UoYcY531UWmdwhRWkcGKnGU93m5HSXP9LP2usOryrBtQowA== -glob@^10.2.2: +glob@^10.2.2, glob@^10.4.1: version "10.4.5" resolved "https://registry.yarnpkg.com/glob/-/glob-10.4.5.tgz#f4d9f0b90ffdbab09c9d77f5f29b4262517b0956" integrity sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg== @@ -3281,6 +3605,11 @@ graphql@^16.8.1: resolved "https://registry.yarnpkg.com/graphql/-/graphql-16.11.0.tgz#96d17f66370678027fdf59b2d4c20b4efaa8a633" integrity sha512-mS1lbMsxgQj6hge1XZ6p7GPhbrtFwUFYi3wRzXAC/FmYnyXMTvvI3td3rjmQ2u8ewXueaSvRPWaEcgVVOT9Jnw== +has-flag@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-4.0.0.tgz#944771fd9c81c81265c4d6941860da06bb59479b" + integrity sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ== + has-property-descriptors@^1.0.0, has-property-descriptors@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz#963ed7d071dc7bf5f084c5bfbe0d1b6222586854" @@ -3319,6 +3648,18 @@ hosted-git-info@^6.0.0, hosted-git-info@^6.1.1: dependencies: lru-cache "^7.5.1" +html-encoding-sniffer@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/html-encoding-sniffer/-/html-encoding-sniffer-4.0.0.tgz#696df529a7cfd82446369dc5193e590a3735b448" + integrity sha512-Y22oTqIU4uuPgEemfz7NDJz6OeKf12Lsu+QC+s3BVpda64lTiMYCyGwg5ki4vFxkMwQdeZDl2adZoqUgdFuTgQ== + dependencies: + whatwg-encoding "^3.1.1" + +html-escaper@^2.0.0: + version "2.0.2" + resolved "https://registry.yarnpkg.com/html-escaper/-/html-escaper-2.0.2.tgz#dfd60027da36a36dfcbe236262c00a5822681453" + integrity sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg== + http-errors@2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-2.0.0.tgz#b7774a1486ef73cf7667ac9ae0858c012c57b9d3" @@ -3330,6 +3671,22 @@ http-errors@2.0.0: statuses "2.0.1" toidentifier "1.0.1" +http-proxy-agent@^7.0.2: + version "7.0.2" + resolved "https://registry.yarnpkg.com/http-proxy-agent/-/http-proxy-agent-7.0.2.tgz#9a8b1f246866c028509486585f62b8f2c18c270e" + integrity sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig== + dependencies: + agent-base "^7.1.0" + debug "^4.3.4" + +https-proxy-agent@^7.0.6: + version "7.0.6" + resolved "https://registry.yarnpkg.com/https-proxy-agent/-/https-proxy-agent-7.0.6.tgz#da8dfeac7da130b05c2ba4b59c9b6cd66611a6b9" + integrity sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw== + dependencies: + agent-base "^7.1.2" + debug "4" + human-signals@^5.0.0: version "5.0.0" resolved "https://registry.yarnpkg.com/human-signals/-/human-signals-5.0.0.tgz#42665a284f9ae0dade3ba41ebc37eb4b852f3a28" @@ -3347,7 +3704,7 @@ iconv-lite@0.4.24: dependencies: safer-buffer ">= 2.1.2 < 3" -iconv-lite@0.6, iconv-lite@^0.6.3: +iconv-lite@0.6, iconv-lite@0.6.3, iconv-lite@^0.6.3: version "0.6.3" resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.6.3.tgz#a52f80bf38da1952eb5c681790719871a1a72501" integrity sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw== @@ -3362,6 +3719,11 @@ import-fresh@^3.3.0: parent-module "^1.0.0" resolve-from "^4.0.0" +indent-string@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/indent-string/-/indent-string-4.0.0.tgz#624f8f4497d619b2d9768531d58f4122854d7251" + integrity sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg== + inherits@2.0.4: version "2.0.4" resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c" @@ -3437,6 +3799,11 @@ is-number@^7.0.0: resolved "https://registry.yarnpkg.com/is-number/-/is-number-7.0.0.tgz#7535345b896734d5f80c4d06c50955527a14f12b" integrity sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng== +is-potential-custom-element-name@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/is-potential-custom-element-name/-/is-potential-custom-element-name-1.0.1.tgz#171ed6f19e3ac554394edf78caa05784a45bebb5" + integrity sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ== + is-regex@^1.1.4: version "1.2.1" resolved "https://registry.yarnpkg.com/is-regex/-/is-regex-1.2.1.tgz#76d70a3ed10ef9be48eb577887d74205bf0cad22" @@ -3462,6 +3829,37 @@ isexe@^2.0.0: resolved "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10" integrity sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw== +istanbul-lib-coverage@^3.0.0, istanbul-lib-coverage@^3.2.2: + version "3.2.2" + resolved "https://registry.yarnpkg.com/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.2.tgz#2d166c4b0644d43a39f04bf6c2edd1e585f31756" + integrity sha512-O8dpsF+r0WV/8MNRKfnmrtCWhuKjxrq2w+jpzBL5UZKTi2LeVWnWOmWRxFlesJONmc+wLAGvKQZEOanko0LFTg== + +istanbul-lib-report@^3.0.0, istanbul-lib-report@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/istanbul-lib-report/-/istanbul-lib-report-3.0.1.tgz#908305bac9a5bd175ac6a74489eafd0fc2445a7d" + integrity sha512-GCfE1mtsHGOELCU8e/Z7YWzpmybrx/+dSTfLrvY8qRmaY6zXTKWn6WQIjaAFw069icm6GVMNkgu0NzI4iPZUNw== + dependencies: + istanbul-lib-coverage "^3.0.0" + make-dir "^4.0.0" + supports-color "^7.1.0" + +istanbul-lib-source-maps@^5.0.6: + version "5.0.6" + resolved "https://registry.yarnpkg.com/istanbul-lib-source-maps/-/istanbul-lib-source-maps-5.0.6.tgz#acaef948df7747c8eb5fbf1265cb980f6353a441" + integrity sha512-yg2d+Em4KizZC5niWhQaIomgf5WlL4vOOjZ5xGCmF8SnPE/mDWWXgvRExdcpCgh9lLRRa1/fSYp2ymmbJ1pI+A== + dependencies: + "@jridgewell/trace-mapping" "^0.3.23" + debug "^4.1.1" + istanbul-lib-coverage "^3.0.0" + +istanbul-reports@^3.1.7: + version "3.1.7" + resolved "https://registry.yarnpkg.com/istanbul-reports/-/istanbul-reports-3.1.7.tgz#daed12b9e1dca518e15c056e1e537e741280fa0b" + integrity sha512-BewmUXImeuRk2YY0PVbxgKAysvhRPUQE0h5QRM++nVWyubKGV0l8qQ5op8+B2DOmwSe63Jivj0BjkPQVf8fP5g== + dependencies: + html-escaper "^2.0.0" + istanbul-lib-report "^3.0.0" + jackspeak@^3.1.2: version "3.4.3" resolved "https://registry.yarnpkg.com/jackspeak/-/jackspeak-3.4.3.tgz#8833a9d89ab4acde6188942bd1c53b6390ed5a8a" @@ -3493,6 +3891,32 @@ js-yaml@^4.1.0: dependencies: argparse "^2.0.1" +jsdom@^26.1.0: + version "26.1.0" + resolved "https://registry.yarnpkg.com/jsdom/-/jsdom-26.1.0.tgz#ab5f1c1cafc04bd878725490974ea5e8bf0c72b3" + integrity sha512-Cvc9WUhxSMEo4McES3P7oK3QaXldCfNWp7pl2NNeiIFlCoLr3kfq9kb1fxftiwk1FLV7CvpvDfonxtzUDeSOPg== + dependencies: + cssstyle "^4.2.1" + data-urls "^5.0.0" + decimal.js "^10.5.0" + html-encoding-sniffer "^4.0.0" + http-proxy-agent "^7.0.2" + https-proxy-agent "^7.0.6" + is-potential-custom-element-name "^1.0.1" + nwsapi "^2.2.16" + parse5 "^7.2.1" + rrweb-cssom "^0.8.0" + saxes "^6.0.0" + symbol-tree "^3.2.4" + tough-cookie "^5.1.1" + w3c-xmlserializer "^5.0.0" + webidl-conversions "^7.0.0" + whatwg-encoding "^3.1.1" + whatwg-mimetype "^4.0.0" + whatwg-url "^14.1.1" + ws "^8.18.0" + xml-name-validator "^5.0.0" + jsesc@3.0.2, jsesc@~3.0.2: version "3.0.2" resolved "https://registry.yarnpkg.com/jsesc/-/jsesc-3.0.2.tgz#bb8b09a6597ba426425f2e4a07245c3d00b9343e" @@ -3659,6 +4083,11 @@ log-update@^6.1.0: strip-ansi "^7.1.0" wrap-ansi "^9.0.0" +loupe@^3.1.0, loupe@^3.1.3: + version "3.1.3" + resolved "https://registry.yarnpkg.com/loupe/-/loupe-3.1.3.tgz#042a8f7986d77f3d0f98ef7990a2b2fef18b0fd2" + integrity sha512-kkIp7XSkP78ZxJEsSxW3712C6teJVoeHHwgo9zJ380de7IYyJ2ISlxojcH2pC5OFLewESmnRi/+XCDIEEVyoug== + lower-case@^2.0.2: version "2.0.2" resolved "https://registry.yarnpkg.com/lower-case/-/lower-case-2.0.2.tgz#6fa237c63dbdc4a82ca0fd882e4722dc5e634e28" @@ -3666,7 +4095,7 @@ lower-case@^2.0.2: dependencies: tslib "^2.0.3" -lru-cache@^10.2.0: +lru-cache@^10.2.0, lru-cache@^10.4.3: version "10.4.3" resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-10.4.3.tgz#410fc8a17b70e598013df257c2446b7f3383f119" integrity sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ== @@ -3683,6 +4112,34 @@ lru-cache@^7.4.4, lru-cache@^7.5.1: resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-7.18.3.tgz#f793896e0fd0e954a59dfdd82f0773808df6aa89" integrity sha512-jumlc0BIUrS3qJGgIkWZsyfAM7NCWiBcCDhnd+3NNM5KbBmLTgHVfWBcg6W+rLUsIpzpERPsvwUP7CckAQSOoA== +lz-string@^1.5.0: + version "1.5.0" + resolved "https://registry.yarnpkg.com/lz-string/-/lz-string-1.5.0.tgz#c1ab50f77887b712621201ba9fd4e3a6ed099941" + integrity sha512-h5bgJWpxJNswbU7qCrV0tIKQCaS3blPDrqKWx+QxzuzL1zGUzij9XCWLrSLsJPu5t+eWA/ycetzYAO5IOMcWAQ== + +magic-string@^0.30.17: + version "0.30.17" + resolved "https://registry.yarnpkg.com/magic-string/-/magic-string-0.30.17.tgz#450a449673d2460e5bbcfba9a61916a1714c7453" + integrity sha512-sNPKHvyjVf7gyjwS4xGTaW/mCnF8wnjtifKBEhxfZ7E/S8tQ0rssrwGNn6q8JH/ohItJfSQp9mBtQYuTlH5QnA== + dependencies: + "@jridgewell/sourcemap-codec" "^1.5.0" + +magicast@^0.3.5: + version "0.3.5" + resolved "https://registry.yarnpkg.com/magicast/-/magicast-0.3.5.tgz#8301c3c7d66704a0771eb1bad74274f0ec036739" + integrity sha512-L0WhttDl+2BOsybvEOLK7fW3UA0OQ0IQ2d6Zl2x/a6vVRs3bAY0ECOSHHeL5jD+SbOpOCUEi0y1DgHEn9Qn1AQ== + dependencies: + "@babel/parser" "^7.25.4" + "@babel/types" "^7.25.4" + source-map-js "^1.2.0" + +make-dir@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/make-dir/-/make-dir-4.0.0.tgz#c3c2307a771277cd9638305f915c29ae741b614e" + integrity sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw== + dependencies: + semver "^7.5.3" + markerjs2@^2.29.4: version "2.32.4" resolved "https://registry.yarnpkg.com/markerjs2/-/markerjs2-2.32.4.tgz#7aa79e87192ae3a8effd4235ae116ed4b8239c31" @@ -3763,6 +4220,11 @@ mimic-function@^5.0.0: resolved "https://registry.yarnpkg.com/mimic-function/-/mimic-function-5.0.1.tgz#acbe2b3349f99b9deaca7fb70e48b83e94e67076" integrity sha512-VP79XUPxV2CigYP3jWwAUFSku2aKqBH7uTAapFWCBqutsbmDo96KY5o8uh6U+/YSIn5OxJnXp73beVkpqMIGhA== +min-indent@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/min-indent/-/min-indent-1.0.1.tgz#a63f681673b30571fbe8bc25686ae746eefa9869" + integrity sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg== + minimatch@^9.0.0, minimatch@^9.0.4: version "9.0.5" resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-9.0.5.tgz#d74f9dd6b57d83d8e98cfb82133b03978bc929e5" @@ -3929,6 +4391,11 @@ nth-check@^2.0.1: dependencies: boolbase "^1.0.0" +nwsapi@^2.2.16: + version "2.2.20" + resolved "https://registry.yarnpkg.com/nwsapi/-/nwsapi-2.2.20.tgz#22e53253c61e7b0e7e93cef42c891154bcca11ef" + integrity sha512-/ieB+mDe4MrrKMT8z+mQL8klXydZWGR5Dowt4RAGKbJ3kIGEx3X4ljUo+6V73IXtUPWgfOlU5B9MlGxFO5T+cA== + object-inspect@^1.13.3: version "1.13.4" resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.13.4.tgz#8375265e21bc20d0fa582c22e1b13485d6e00213" @@ -4012,6 +4479,13 @@ parse-json@^5.2.0: json-parse-even-better-errors "^2.3.0" lines-and-columns "^1.1.6" +parse5@^7.2.1: + version "7.3.0" + resolved "https://registry.yarnpkg.com/parse5/-/parse5-7.3.0.tgz#d7e224fa72399c7a175099f45fc2ad024b05ec05" + integrity sha512-IInvU7fabl34qmi9gY8XOVxhYyMyuH2xUNpb2q8/Y+7552KlejkRvqvD19nMoUW/uQGGbqNpA6Tufu5FL5BZgw== + dependencies: + entities "^6.0.0" + parseurl@~1.3.3: version "1.3.3" resolved "https://registry.yarnpkg.com/parseurl/-/parseurl-1.3.3.tgz#9da19e7bee8d12dff0513ed5b76957793bc2e8d4" @@ -4060,6 +4534,16 @@ pathe@^1.1.2: resolved "https://registry.yarnpkg.com/pathe/-/pathe-1.1.2.tgz#6c4cb47a945692e48a1ddd6e4094d170516437ec" integrity sha512-whLdWMYL2TwI08hn8/ZqAbrVemu0LNaNNJZX73O6qaIdCTfXutsLhMkjdENX0qhsQ9uIimo4/aQOmXkoon2nDQ== +pathe@^2.0.3: + version "2.0.3" + resolved "https://registry.yarnpkg.com/pathe/-/pathe-2.0.3.tgz#3ecbec55421685b70a9da872b2cff3e1cbed1716" + integrity sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w== + +pathval@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/pathval/-/pathval-2.0.0.tgz#7e2550b422601d4f6b8e26f1301bc8f15a741a25" + integrity sha512-vE7JKRyES09KiunauX7nd2Q9/L7lhok4smP9RZTDeD4MVs72Dp2qNFVz39Nz5a0FVEW0BJR6C0DYrq6unoziZA== + pdfmake@^0.2.2: version "0.2.19" resolved "https://registry.yarnpkg.com/pdfmake/-/pdfmake-0.2.19.tgz#23d6862b395de95e41089263936f0ff806193ea7" @@ -4116,6 +4600,15 @@ prettier@^2.7.1: resolved "https://registry.yarnpkg.com/prettier/-/prettier-2.8.8.tgz#e8c5d7e98a4305ffe3de2e1fc4aca1a71c28b1da" integrity sha512-tdN8qQGvNjw4CHbY+XXk0JgCXn9QiF21a55rBe5LJAU+kDyC4WQn4+awm2Xfk2lQMk5fKup9XgzTZtGkjBdP9Q== +pretty-format@^27.0.2: + version "27.5.1" + resolved "https://registry.yarnpkg.com/pretty-format/-/pretty-format-27.5.1.tgz#2181879fdea51a7a5851fb39d920faa63f01d88e" + integrity sha512-Qb1gy5OrP5+zDf2Bvnzdl3jsTf1qXVMazbvCoKhtKqVs4/YK4ozX4gKQJJVyNe+cajNPn0KoC0MC3FUmaHWEmQ== + dependencies: + ansi-regex "^5.0.1" + ansi-styles "^5.0.0" + react-is "^17.0.1" + proc-log@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/proc-log/-/proc-log-3.0.0.tgz#fb05ef83ccd64fd7b20bbe9c8c1070fc08338dd8" @@ -4188,6 +4681,11 @@ react-dom@^19.1.0: dependencies: scheduler "^0.26.0" +react-is@^17.0.1: + version "17.0.2" + resolved "https://registry.yarnpkg.com/react-is/-/react-is-17.0.2.tgz#e691d4a8e9c789365655539ab372762b0efb54f0" + integrity sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w== + react-refresh@^0.14.0: version "0.14.2" resolved "https://registry.yarnpkg.com/react-refresh/-/react-refresh-0.14.2.tgz#3833da01ce32da470f1f936b9d477da5c7028bf9" @@ -4218,6 +4716,14 @@ readdirp@^4.0.1: resolved "https://registry.yarnpkg.com/readdirp/-/readdirp-4.1.2.tgz#eb85801435fbf2a7ee58f19e0921b068fc69948d" integrity sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg== +redent@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/redent/-/redent-3.0.0.tgz#e557b7998316bb53c9f1f56fa626352c6963059f" + integrity sha512-6tDA8g98We0zd0GvVeMT9arEOnTw9qM03L9cJXaCjrip1OO764RDBLBfrB4cwzNGDj5OA5ioymC9GkizgWJDUg== + dependencies: + indent-string "^4.0.0" + strip-indent "^3.0.0" + regenerate-unicode-properties@^10.2.0: version "10.2.0" resolved "https://registry.yarnpkg.com/regenerate-unicode-properties/-/regenerate-unicode-properties-10.2.0.tgz#626e39df8c372338ea9b8028d1f99dc3fd9c3db0" @@ -4342,6 +4848,11 @@ rollup@^4.34.9: "@rollup/rollup-win32-x64-msvc" "4.40.2" fsevents "~2.3.2" +rrweb-cssom@^0.8.0: + version "0.8.0" + resolved "https://registry.yarnpkg.com/rrweb-cssom/-/rrweb-cssom-0.8.0.tgz#3021d1b4352fbf3b614aaeed0bc0d5739abe0bc2" + integrity sha512-guoltQEx+9aMf2gDZ0s62EcV8lsXR+0w8915TC3ITdn2YueuNjdAYh/levpU9nFaoChh9RUS5ZdQMrKfVEN9tw== + rw@1: version "1.3.3" resolved "https://registry.yarnpkg.com/rw/-/rw-1.3.3.tgz#3f862dfa91ab766b14885ef4d01124bfda074fb4" @@ -4367,6 +4878,13 @@ sax@^1.2.4: resolved "https://registry.yarnpkg.com/sax/-/sax-1.4.1.tgz#44cc8988377f126304d3b3fc1010c733b929ef0f" integrity sha512-+aWOz7yVScEGoKNd4PA10LZ8sk0A/z5+nXQG5giUO5rprX9jgYsTdov9qCchZiPIZezbZH+jRut8nPodFAX4Jg== +saxes@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/saxes/-/saxes-6.0.0.tgz#fe5b4a4768df4f14a201b1ba6a65c1f3d9988cc5" + integrity sha512-xAg7SOnEhrm5zI3puOOKyy1OMcMlIJZYNJY7xLBwSze0UjhPLnWfj2GF2EpT0jmzaJKIWKHLsaSSajf35bcYnA== + dependencies: + xmlchars "^2.2.0" + scheduler@^0.26.0: version "0.26.0" resolved "https://registry.yarnpkg.com/scheduler/-/scheduler-0.26.0.tgz#4ce8a8c2a2095f13ea11bf9a445be50c555d6337" @@ -4500,6 +5018,11 @@ side-channel@^1.0.6: side-channel-map "^1.0.1" side-channel-weakmap "^1.0.2" +siginfo@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/siginfo/-/siginfo-2.0.0.tgz#32e76c70b79724e3bb567cb9d543eb858ccfaf30" + integrity sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g== + signal-exit@^4.0.1, signal-exit@^4.1.0: version "4.1.0" resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-4.1.0.tgz#952188c1cbd546070e2dd20d0f41c0ae0530cb04" @@ -4529,7 +5052,7 @@ snake-case@^3.0.4: dot-case "^3.0.4" tslib "^2.0.3" -source-map-js@^1.0.1, source-map-js@^1.2.1: +source-map-js@^1.0.1, source-map-js@^1.2.0, source-map-js@^1.2.1: version "1.2.1" resolved "https://registry.yarnpkg.com/source-map-js/-/source-map-js-1.2.1.tgz#1ce5650fddd87abc099eda37dcff024c2667ae46" integrity sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA== @@ -4573,11 +5096,21 @@ spdx-license-ids@^3.0.0: resolved "https://registry.yarnpkg.com/spdx-license-ids/-/spdx-license-ids-3.0.21.tgz#6d6e980c9df2b6fc905343a3b2d702a6239536c3" integrity sha512-Bvg/8F5XephndSK3JffaRqdT+gyhfqIPwDHpX80tJrF8QQRYMo8sNMeaZ2Dp5+jhwKnUmIOyFFQfHRkjJm5nXg== +stackback@0.0.2: + version "0.0.2" + resolved "https://registry.yarnpkg.com/stackback/-/stackback-0.0.2.tgz#1ac8a0d9483848d1695e418b6d031a3c3ce68e3b" + integrity sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw== + statuses@2.0.1, statuses@^2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/statuses/-/statuses-2.0.1.tgz#55cb000ccf1d48728bd23c685a063998cf1a1b63" integrity sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ== +std-env@^3.9.0: + version "3.9.0" + resolved "https://registry.yarnpkg.com/std-env/-/std-env-3.9.0.tgz#1a6f7243b339dca4c9fd55e1c7504c77ef23e8f1" + integrity sha512-UGvjygr6F6tpH7o2qyqR6QYpwraIjKSdtzyBdyytFOHmPZY917kwdwLG0RbOjWOnKmnm3PeHjaoLLMie7kPLQw== + stream-slice@^0.1.2: version "0.1.2" resolved "https://registry.yarnpkg.com/stream-slice/-/stream-slice-0.1.2.tgz#2dc4f4e1b936fb13f3eb39a2def1932798d07a4b" @@ -4655,6 +5188,20 @@ strip-final-newline@^3.0.0: resolved "https://registry.yarnpkg.com/strip-final-newline/-/strip-final-newline-3.0.0.tgz#52894c313fbff318835280aed60ff71ebf12b8fd" integrity sha512-dOESqjYr96iWYylGObzd39EuNTa5VJxyvVAEm5Jnh7KGo75V43Hk1odPQkNDyXNmUR6k+gEiDVXnjB8HJ3crXw== +strip-indent@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/strip-indent/-/strip-indent-3.0.0.tgz#c32e1cee940b6b3432c771bc2c54bcce73cd3001" + integrity sha512-laJTa3Jb+VQpaC6DseHhF7dXVqHTfJPCRDaEbid/drOhgitgYku/letMUqOXFoWV0zIIUbjpdH2t+tYj4bQMRQ== + dependencies: + min-indent "^1.0.0" + +supports-color@^7.1.0: + version "7.2.0" + resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-7.2.0.tgz#1b7dcdcb32b8138801b3e478ba6a51caa89648da" + integrity sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw== + dependencies: + has-flag "^4.0.0" + supports-preserve-symlinks-flag@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz#6eda4bd344a3c94aea376d4cc31bc77311039e09" @@ -4683,6 +5230,11 @@ svgo@^3.0.2: csso "^5.0.5" picocolors "^1.0.0" +symbol-tree@^3.2.4: + version "3.2.4" + resolved "https://registry.yarnpkg.com/symbol-tree/-/symbol-tree-3.2.4.tgz#430637d248ba77e078883951fb9aa0eed7c63fa2" + integrity sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw== + tailwindcss@4.1.5, tailwindcss@^4.1.5: version "4.1.5" resolved "https://registry.yarnpkg.com/tailwindcss/-/tailwindcss-4.1.5.tgz#d35607f1a351051bd29cda7e59ab2c222ca8deb6" @@ -4693,11 +5245,30 @@ tapable@^2.2.0: resolved "https://registry.yarnpkg.com/tapable/-/tapable-2.2.1.tgz#1967a73ef4060a82f12ab96af86d52fdb76eeca0" integrity sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ== +test-exclude@^7.0.1: + version "7.0.1" + resolved "https://registry.yarnpkg.com/test-exclude/-/test-exclude-7.0.1.tgz#20b3ba4906ac20994e275bbcafd68d510264c2a2" + integrity sha512-pFYqmTw68LXVjeWJMST4+borgQP2AyMNbg1BpZh9LbyhUeNkeaPF9gzfPGUAnSMV3qPYdWUwDIjjCLiSDOl7vg== + dependencies: + "@istanbuljs/schema" "^0.1.2" + glob "^10.4.1" + minimatch "^9.0.4" + tiny-inflate@^1.0.0, tiny-inflate@^1.0.2: version "1.0.3" resolved "https://registry.yarnpkg.com/tiny-inflate/-/tiny-inflate-1.0.3.tgz#122715494913a1805166aaf7c93467933eea26c4" integrity sha512-pkY1fj1cKHb2seWDy0B16HeWyczlJA9/WW3u3c4z/NiWDsO3DOU5D7nhTLE9CF0yXv/QZFY7sEJmj24dK+Rrqw== +tinybench@^2.9.0: + version "2.9.0" + resolved "https://registry.yarnpkg.com/tinybench/-/tinybench-2.9.0.tgz#103c9f8ba6d7237a47ab6dd1dcff77251863426b" + integrity sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg== + +tinyexec@^0.3.2: + version "0.3.2" + resolved "https://registry.yarnpkg.com/tinyexec/-/tinyexec-0.3.2.tgz#941794e657a85e496577995c6eef66f53f42b3d2" + integrity sha512-KQQR9yN7R5+OSwaK0XQoj22pwHoTlgYqmUscPYoknOoWCWfj/5/ABTMRi69FrKU5ffPVh5QcFikpWJI/P1ocHA== + tinyglobby@^0.2.13: version "0.2.13" resolved "https://registry.yarnpkg.com/tinyglobby/-/tinyglobby-0.2.13.tgz#a0e46515ce6cbcd65331537e57484af5a7b2ff7e" @@ -4706,11 +5277,38 @@ tinyglobby@^0.2.13: fdir "^6.4.4" picomatch "^4.0.2" +tinypool@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/tinypool/-/tinypool-1.0.2.tgz#706193cc532f4c100f66aa00b01c42173d9051b2" + integrity sha512-al6n+QEANGFOMf/dmUMsuS5/r9B06uwlyNjZZql/zv8J7ybHCgoihBNORZCY2mzUuAnomQa2JdhyHKzZxPCrFA== + tinyqueue@^2.0.3: version "2.0.3" resolved "https://registry.yarnpkg.com/tinyqueue/-/tinyqueue-2.0.3.tgz#64d8492ebf39e7801d7bd34062e29b45b2035f08" integrity sha512-ppJZNDuKGgxzkHihX8v9v9G5f+18gzaTfrukGrq6ueg0lmH4nqVnA2IPG0AEH3jKEk2GRJCUhDoqpoiw3PHLBA== +tinyrainbow@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/tinyrainbow/-/tinyrainbow-2.0.0.tgz#9509b2162436315e80e3eee0fcce4474d2444294" + integrity sha512-op4nsTR47R6p0vMUUoYl/a+ljLFVtlfaXkLQmqfLR1qHma1h/ysYk4hEXZ880bf2CYgTskvTa/e196Vd5dDQXw== + +tinyspy@^3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/tinyspy/-/tinyspy-3.0.2.tgz#86dd3cf3d737b15adcf17d7887c84a75201df20a" + integrity sha512-n1cw8k1k0x4pgA2+9XrOkFydTerNcJ1zWCO5Nn9scWHTD+5tp8dghT2x1uduQePZTZgd3Tupf+x9BxJjeJi77Q== + +tldts-core@^6.1.86: + version "6.1.86" + resolved "https://registry.yarnpkg.com/tldts-core/-/tldts-core-6.1.86.tgz#a93e6ed9d505cb54c542ce43feb14c73913265d8" + integrity sha512-Je6p7pkk+KMzMv2XXKmAE3McmolOQFdxkKw0R8EYNr7sELW46JqnNeTX8ybPiQgvg1ymCoF8LXs5fzFaZvJPTA== + +tldts@^6.1.32: + version "6.1.86" + resolved "https://registry.yarnpkg.com/tldts/-/tldts-6.1.86.tgz#087e0555b31b9725ee48ca7e77edc56115cd82f7" + integrity sha512-WMi/OQ2axVTf/ykqCQgXiIct+mSQDFdH2fkwhPwgEwvJ1kSzZRiinb0zF2Xb8u4+OqPChmyI6MEu4EezNJz+FQ== + dependencies: + tldts-core "^6.1.86" + to-regex-range@^5.0.1: version "5.0.1" resolved "https://registry.yarnpkg.com/to-regex-range/-/to-regex-range-5.0.1.tgz#1648c44aae7c8d988a326018ed72f5b4dd0392e4" @@ -4733,6 +5331,20 @@ tough-cookie@^4.1.4: universalify "^0.2.0" url-parse "^1.5.3" +tough-cookie@^5.1.1: + version "5.1.2" + resolved "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-5.1.2.tgz#66d774b4a1d9e12dc75089725af3ac75ec31bed7" + integrity sha512-FVDYdxtnj0G6Qm/DhNPSb8Ju59ULcup3tuJxkFb5K8Bv2pUXILbf0xZWU8PX8Ov19OXljbUyveOFwRMwkXzO+A== + dependencies: + tldts "^6.1.32" + +tr46@^5.1.0: + version "5.1.1" + resolved "https://registry.yarnpkg.com/tr46/-/tr46-5.1.1.tgz#96ae867cddb8fdb64a49cc3059a8d428bcf238ca" + integrity sha512-hdF5ZgjTqgAntKkklYw0R03MG2x/bSzTtkxmIRw/sTNV8YXsCJ1tfLAX23lhxhHJlEf3CRCOCGGWw3vI3GaSPw== + dependencies: + punycode "^2.3.1" + tsconfck@^3.0.3: version "3.1.5" resolved "https://registry.yarnpkg.com/tsconfck/-/tsconfck-3.1.5.tgz#2f07f9be6576825e7a77470a5304ce06c7746e61" @@ -4895,6 +5507,17 @@ vite-node@3.0.0-beta.2: pathe "^1.1.2" vite "^5.0.0 || ^6.0.0" +vite-node@3.1.4: + version "3.1.4" + resolved "https://registry.yarnpkg.com/vite-node/-/vite-node-3.1.4.tgz#13f10b2cb155197a971cb2761664ec952c6cae18" + integrity sha512-6enNwYnpyDo4hEgytbmc6mYWHXDHYEn0D1/rw4Q+tnHUGtKTJsn8T1YkX6Q18wI5LCrS8CTYlBaiCqxOy2kvUA== + dependencies: + cac "^6.7.14" + debug "^4.4.0" + es-module-lexer "^1.7.0" + pathe "^2.0.3" + vite "^5.0.0 || ^6.0.0" + vite-plugin-svgr@^4.3.0: version "4.3.0" resolved "https://registry.yarnpkg.com/vite-plugin-svgr/-/vite-plugin-svgr-4.3.0.tgz#742f16f11375996306c696ec323e4d23f6005075" @@ -4927,6 +5550,65 @@ vite-tsconfig-paths@^5.1.4: optionalDependencies: fsevents "~2.3.3" +vitest@^3.1.4: + version "3.1.4" + resolved "https://registry.yarnpkg.com/vitest/-/vitest-3.1.4.tgz#5f495b7dbb1d4d208b88508cd4dfceb006f8b7e6" + integrity sha512-Ta56rT7uWxCSJXlBtKgIlApJnT6e6IGmTYxYcmxjJ4ujuZDI59GUQgVDObXXJujOmPDBYXHK1qmaGtneu6TNIQ== + dependencies: + "@vitest/expect" "3.1.4" + "@vitest/mocker" "3.1.4" + "@vitest/pretty-format" "^3.1.4" + "@vitest/runner" "3.1.4" + "@vitest/snapshot" "3.1.4" + "@vitest/spy" "3.1.4" + "@vitest/utils" "3.1.4" + chai "^5.2.0" + debug "^4.4.0" + expect-type "^1.2.1" + magic-string "^0.30.17" + pathe "^2.0.3" + std-env "^3.9.0" + tinybench "^2.9.0" + tinyexec "^0.3.2" + tinyglobby "^0.2.13" + tinypool "^1.0.2" + tinyrainbow "^2.0.0" + vite "^5.0.0 || ^6.0.0" + vite-node "3.1.4" + why-is-node-running "^2.3.0" + +w3c-xmlserializer@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/w3c-xmlserializer/-/w3c-xmlserializer-5.0.0.tgz#f925ba26855158594d907313cedd1476c5967f6c" + integrity sha512-o8qghlI8NZHU1lLPrpi2+Uq7abh4GGPpYANlalzWxyWteJOCsr/P+oPBA49TOLu5FTZO4d3F9MnWJfiMo4BkmA== + dependencies: + xml-name-validator "^5.0.0" + +webidl-conversions@^7.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-7.0.0.tgz#256b4e1882be7debbf01d05f0aa2039778ea080a" + integrity sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g== + +whatwg-encoding@^3.1.1: + version "3.1.1" + resolved "https://registry.yarnpkg.com/whatwg-encoding/-/whatwg-encoding-3.1.1.tgz#d0f4ef769905d426e1688f3e34381a99b60b76e5" + integrity sha512-6qN4hJdMwfYBtE3YBTTHhoeuUrDBPZmbQaxWAqSALV/MeEnR5z1xd8UKud2RAkFoPkmB+hli1TZSnyi84xz1vQ== + dependencies: + iconv-lite "0.6.3" + +whatwg-mimetype@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/whatwg-mimetype/-/whatwg-mimetype-4.0.0.tgz#bc1bf94a985dc50388d54a9258ac405c3ca2fc0a" + integrity sha512-QaKxh0eNIi2mE9p2vEdzfagOKHCcj1pJ56EEHGQOVxp8r9/iszLUUV7v89x9O1p/T+NlTM5W7jW6+cz4Fq1YVg== + +whatwg-url@^14.0.0, whatwg-url@^14.1.1: + version "14.2.0" + resolved "https://registry.yarnpkg.com/whatwg-url/-/whatwg-url-14.2.0.tgz#4ee02d5d725155dae004f6ae95c73e7ef5d95663" + integrity sha512-De72GdQZzNTUBBChsXueQUnPKDkg/5A5zp7pFDuQAj5UFoENpiACU0wlCvzpAGnTkj++ihpKwKyYewn/XNUbKw== + dependencies: + tr46 "^5.1.0" + webidl-conversions "^7.0.0" + which@^2.0.1: version "2.0.2" resolved "https://registry.yarnpkg.com/which/-/which-2.0.2.tgz#7c6a8dd0a636a0327e10b59c9286eee93f3f51b1" @@ -4941,6 +5623,14 @@ which@^3.0.0: dependencies: isexe "^2.0.0" +why-is-node-running@^2.3.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/why-is-node-running/-/why-is-node-running-2.3.0.tgz#a3f69a97107f494b3cdc3bdddd883a7d65cebf04" + integrity sha512-hUrmaWBdVDcxvYqnyh09zunKzROWjbZTiNy8dBEjkS7ehEDQibXJ7XvlmtbwuTclUiIyN+CyXQD4Vmko8fNm8w== + dependencies: + siginfo "^2.0.0" + stackback "0.0.2" + "wrap-ansi-cjs@npm:wrap-ansi@^7.0.0": version "7.0.0" resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43" @@ -4986,11 +5676,21 @@ wrap-ansi@^9.0.0: string-width "^7.0.0" strip-ansi "^7.1.0" -ws@^8.18.2: +ws@^8.18.0, ws@^8.18.2: version "8.18.2" resolved "https://registry.yarnpkg.com/ws/-/ws-8.18.2.tgz#42738b2be57ced85f46154320aabb51ab003705a" integrity sha512-DMricUmwGZUVr++AEAe2uiVM7UoO9MAVZMDu05UQOaUII0lp+zOzLLU4Xqh/JvTqklB1T4uELaaPBKyjE1r4fQ== +xml-name-validator@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/xml-name-validator/-/xml-name-validator-5.0.0.tgz#82be9b957f7afdacf961e5980f1bf227c0bf7673" + integrity sha512-EvGK8EJ3DhaHfbRlETOWAS5pO9MZITeauHKJyb8wyajUfQUenkIg2MvLDTZ4T/TgIcm3HU0TFBgWWboAZ30UHg== + +xmlchars@^2.2.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/xmlchars/-/xmlchars-2.2.0.tgz#060fe1bcb7f9c76fe2a17db86a9bc3ab894210cb" + integrity sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw== + xmldoc@^2.0.0: version "2.0.1" resolved "https://registry.yarnpkg.com/xmldoc/-/xmldoc-2.0.1.tgz#a901f6a6341e4d8cba3dbc5fc61017249f2adf24"