From 172039ca3f9b23a5d4dd003178e0efaa3caa7e40 Mon Sep 17 00:00:00 2001
From: umsungjun <99730910+umsungjun@users.noreply.github.com>
Date: Wed, 14 Jan 2026 15:56:19 +0900
Subject: [PATCH] feat: set up Vitest with React Testing Library and CI/CD
pipeline (#5)
- Add Vitest and React Testing Library dependencies
- Create comprehensive test suite with 19 tests (100% coverage)
- Configure GitHub Actions workflow for automated testing
- Integrate Codecov for coverage tracking
- Add test scripts to package.json
- Update documentation with testing guide
- Add Korean README (README.ko.md)
---
.github/workflows/ci.yml | 76 ++
.gitignore | 4 +
README.ko.md | 144 ++++
README.md | 35 +
package.json | 15 +-
pnpm-lock.yaml | 1315 ++++++++++++++++++++++++++++++-
src/test/ReactHeadSafe.test.tsx | 247 ++++++
src/test/setup.ts | 12 +
vitest.config.ts | 29 +
9 files changed, 1861 insertions(+), 16 deletions(-)
create mode 100644 .github/workflows/ci.yml
create mode 100644 README.ko.md
create mode 100644 src/test/ReactHeadSafe.test.tsx
create mode 100644 src/test/setup.ts
create mode 100644 vitest.config.ts
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
new file mode 100644
index 0000000..a794e0d
--- /dev/null
+++ b/.github/workflows/ci.yml
@@ -0,0 +1,76 @@
+name: CI
+
+on:
+ push:
+ branches: [main]
+ pull_request:
+ branches: [main]
+
+jobs:
+ test:
+ runs-on: ubuntu-latest
+
+ steps:
+ - name: Checkout code
+ uses: actions/checkout@v4
+
+ - name: Setup pnpm
+ uses: pnpm/action-setup@v4
+ with:
+ version: 10.5.2
+
+ - name: Setup Node.js
+ uses: actions/setup-node@v4
+ with:
+ node-version: '20.x'
+ cache: 'pnpm'
+
+ - name: Install dependencies
+ run: pnpm install --frozen-lockfile
+
+ - name: Run tests
+ run: pnpm test
+
+ - name: Run tests with coverage
+ run: pnpm test:coverage
+
+ - name: Upload coverage to Codecov
+ uses: codecov/codecov-action@v4
+ with:
+ token: ${{ secrets.CODECOV_TOKEN }}
+ files: ./coverage/coverage-final.json
+ flags: unittests
+ name: codecov-umbrella
+ fail_ci_if_error: false
+
+ build:
+ runs-on: ubuntu-latest
+ needs: test
+
+ steps:
+ - name: Checkout code
+ uses: actions/checkout@v4
+
+ - name: Setup pnpm
+ uses: pnpm/action-setup@v4
+ with:
+ version: 10.5.2
+
+ - name: Setup Node.js
+ uses: actions/setup-node@v4
+ with:
+ node-version: '20.x'
+ cache: 'pnpm'
+
+ - name: Install dependencies
+ run: pnpm install --frozen-lockfile
+
+ - name: Build package
+ run: pnpm build
+
+ - name: Check for build artifacts
+ run: |
+ if [ ! -d "dist" ]; then
+ echo "Build failed: dist directory not found"
+ exit 1
+ fi
diff --git a/.gitignore b/.gitignore
index 5d2a3df..bb2e943 100644
--- a/.gitignore
+++ b/.gitignore
@@ -4,3 +4,7 @@ dist
.DS_Store
.env
.env.local
+
+# Test coverage
+coverage
+.vitest
diff --git a/README.ko.md b/README.ko.md
new file mode 100644
index 0000000..56cf4cc
--- /dev/null
+++ b/README.ko.md
@@ -0,0 +1,144 @@
+# react-head-safe
+
+[English](README.md) | [한국어](README.ko.md)
+
+중복 메타 태그를 방지하고 document head 요소를 안전하게 관리하는 CSR 전용 React head 매니저입니다.
+
+## 왜 react-head-safe인가요?
+
+React에서 document head 요소를 관리하기 위한 가볍고 CSR에 최적화된 대안입니다. 다음과 같은 요구사항이 있는 간단한 클라이언트 사이드 렌더링 애플리케이션에 완벽합니다:
+
+- **명시적 중복 방지** - 새로운 태그를 추가하기 전에 항상 기존 메타 태그를 제거합니다
+- **간단함** - 복잡한 설정 없이 props만 전달하는 단일 컴포넌트
+- **성능** - 페인트 전 동기적 DOM 업데이트를 위해 `useLayoutEffect` 사용
+- **타입 안전성** - TypeScript로 작성되어 완전한 타입 정의 제공
+
+## 주요 기능
+
+- ✅ **중복 태그 없음** - 새 태그를 만들기 전에 기존 메타 태그 제거
+- ✅ **TypeScript 지원** - TypeScript로 완전한 타입 안전성 제공
+- ✅ **경량** - React 외에 의존성 제로
+- ✅ **CSR 최적화** - 동기적 DOM 업데이트를 위해 `useLayoutEffect` 사용
+- ✅ **Open Graph 지원** - 소셜 미디어 메타 태그 기본 지원
+- ✅ **간단한 API** - props만 전달하면 되는 복잡하지 않은 설정
+
+## 설치
+
+[](https://www.npmjs.com/package/react-head-safe)
+
+```bash
+npm install react-head-safe
+# 또는
+yarn add react-head-safe
+# 또는
+pnpm add react-head-safe
+```
+
+## 사용법
+
+```tsx
+import { ReactHeadSafe } from 'react-head-safe';
+
+function MyPage() {
+ return (
+ <>
+
+
페이지 콘텐츠...
+ >
+ );
+}
+```
+
+## API
+
+### ReactHeadSafeProps
+
+| Prop | Type | Description |
+| --------------- | -------- | -------------------------------------------------------- |
+| `title` | `string` | `document.title`에 설정될 페이지 제목 |
+| `description` | `string` | SEO를 위한 메타 설명 태그 콘텐츠 |
+| `keywords` | `string` | SEO를 위한 메타 키워드 태그 콘텐츠 |
+| `ogTitle` | `string` | 소셜 미디어 공유를 위한 Open Graph 제목 (og:title) |
+| `ogDescription` | `string` | 소셜 미디어 공유를 위한 Open Graph 설명 (og:description) |
+| `ogImage` | `string` | 소셜 미디어 공유를 위한 Open Graph 이미지 URL (og:image) |
+
+## 언제 사용해야 하나요?
+
+**다음과 같은 경우 react-head-safe를 사용하세요:**
+
+- CSR 전용 애플리케이션을 구축하는 경우 (Create React App, Vite 등)
+- 최소한의 설정으로 간단하고 가벼운 솔루션을 원하는 경우
+- 중복 메타 태그 방지에 대한 명시적 제어가 필요한 경우
+- 동기적 DOM 업데이트를 위해 `useLayoutEffect`를 선호하는 경우
+
+**다음과 같은 경우 대안을 사용하세요:**
+
+- SSR/SSG 지원이 필요한 경우 → `react-helmet-async`, Next.js ``, Remix `` 사용
+- 중첩 컴포넌트나 중복 제거 로직 같은 고급 기능이 필요한 경우
+- head 관리 기능이 내장된 프레임워크를 이미 사용 중인 경우
+
+## 왜 CSR 전용인가요?
+
+이 라이브러리는 **클라이언트 사이드 렌더링(CSR)** 애플리케이션을 위해 특별히 설계되었습니다. 서버 사이드 렌더링(SSR) 지원이 필요하다면 다음을 고려하세요:
+
+- SSR용 `react-helmet-async`
+- Next.js 내장 `` 컴포넌트
+- Remix `` 컴포넌트
+
+## 로컬 개발
+
+예제 애플리케이션으로 로컬 변경사항을 테스트하려면:
+
+```bash
+pnpm run example
+```
+
+이 명령은 라이브러리의 로컬 버전을 사용하여 예제 프로젝트를 실행하므로, 게시하기 전에 수정한 내용을 테스트할 수 있습니다.
+
+## 테스트
+
+[](https://codecov.io/gh/umsungjun/react-head-safe)
+
+이 프로젝트는 단위 테스트를 위해 **Vitest**와 **React Testing Library**를 사용합니다.
+
+### 테스트 실행
+
+```bash
+# 테스트 한 번 실행
+pnpm test
+
+# watch 모드로 테스트 실행
+pnpm test:watch
+
+# 커버리지와 함께 테스트 실행
+pnpm test:coverage
+
+# UI 모드로 테스트 실행
+pnpm test:ui
+```
+
+### CI/CD
+
+이 프로젝트는 지속적 통합을 위해 GitHub Actions를 사용합니다:
+
+- ✅ **자동화된 테스트** - 모든 PR과 main 브랜치 push 시 테스트 실행
+- ✅ **Node.js 20.x** - Node.js 20.x (LTS) 버전으로 테스트
+- ✅ **빌드 검증** - 패키지가 올바르게 빌드되는지 확인
+- ✅ **브랜치 보호** - main 브랜치에 머지하기 전에 테스트 통과 필수
+
+PR이 main 브랜치에 머지되기 전에 모든 테스트를 통과해야 합니다.
+
+## 라이선스
+
+[MIT](LICENSE)
+
+## 기여하기
+
+기여를 환영합니다! Pull Request를 자유롭게 제출해주세요.
diff --git a/README.md b/README.md
index 0d257da..6b9e296 100644
--- a/README.md
+++ b/README.md
@@ -1,5 +1,7 @@
# react-head-safe
+[English](README.md) | [한국어](README.ko.md)
+
A CSR-only React head manager that prevents duplicate meta tags and safely manages document head elements.
## Why react-head-safe?
@@ -98,6 +100,39 @@ To test your local changes with the example application:
pnpm run example
```
+## Testing
+
+[](https://codecov.io/gh/umsungjun/react-head-safe)
+
+This project uses **Vitest** and **React Testing Library** for unit testing.
+
+### Running Tests
+
+```bash
+# Run tests once
+pnpm test
+
+# Run tests in watch mode
+pnpm test:watch
+
+# Run tests with coverage
+pnpm test:coverage
+
+# Run tests with UI
+pnpm test:ui
+```
+
+### CI/CD
+
+The project uses GitHub Actions for continuous integration:
+
+- ✅ **Automated Testing** - Tests run on every PR and push to main
+- ✅ **Node.js 20.x** - Tests against Node.js 20.x (LTS)
+- ✅ **Build Verification** - Ensures the package builds correctly
+- ✅ **Branch Protection** - Tests must pass before merging to main
+
+All tests must pass before a PR can be merged to the main branch.
+
This command will run the example project using your local version of the library, allowing you to test any modifications you've made before publishing.
## License
diff --git a/package.json b/package.json
index e05d012..48813f4 100644
--- a/package.json
+++ b/package.json
@@ -22,18 +22,29 @@
"build": "vite build",
"prepublishOnly": "pnpm run build",
"format": "prettier --write \"src/**/*.{ts,tsx}\"",
- "example": "cd examples/basic && pnpm install && pnpm dev"
+ "example": "cd examples/basic && pnpm install && pnpm dev",
+ "test": "vitest run",
+ "test:watch": "vitest",
+ "test:coverage": "vitest run --coverage",
+ "test:ui": "vitest --ui"
},
"peerDependencies": {
"react": ">=17"
},
"devDependencies": {
+ "@testing-library/jest-dom": "^6.9.1",
+ "@testing-library/react": "^16.3.1",
"@types/node": "^22.10.5",
"@types/react": "^19.2.7",
+ "@vitejs/plugin-react": "^5.1.2",
+ "@vitest/coverage-v8": "^4.0.17",
+ "happy-dom": "^20.1.0",
+ "jsdom": "^27.4.0",
"prettier": "^3.7.4",
"typescript": "^5.9.3",
"vite": "^6.0.7",
- "vite-plugin-dts": "^4.4.2"
+ "vite-plugin-dts": "^4.4.2",
+ "vitest": "^4.0.17"
},
"packageManager": "pnpm@10.5.2",
"keywords": [
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index 14b122c..70e1e23 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -12,12 +12,30 @@ importers:
specifier: '>=17'
version: 19.2.3
devDependencies:
+ '@testing-library/jest-dom':
+ specifier: ^6.9.1
+ version: 6.9.1
+ '@testing-library/react':
+ specifier: ^16.3.1
+ version: 16.3.1(@testing-library/dom@10.4.1)(@types/react@19.2.7)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)
'@types/node':
specifier: ^22.10.5
version: 22.19.3
'@types/react':
specifier: ^19.2.7
version: 19.2.7
+ '@vitejs/plugin-react':
+ specifier: ^5.1.2
+ version: 5.1.2(vite@6.4.1(@types/node@22.19.3))
+ '@vitest/coverage-v8':
+ specifier: ^4.0.17
+ version: 4.0.17(vitest@4.0.17(@types/node@22.19.3)(happy-dom@20.1.0)(jsdom@27.4.0))
+ happy-dom:
+ specifier: ^20.1.0
+ version: 20.1.0
+ jsdom:
+ specifier: ^27.4.0
+ version: 27.4.0
prettier:
specifier: ^3.7.4
version: 3.7.4
@@ -30,9 +48,65 @@ importers:
vite-plugin-dts:
specifier: ^4.4.2
version: 4.5.4(@types/node@22.19.3)(rollup@4.55.1)(typescript@5.9.3)(vite@6.4.1(@types/node@22.19.3))
+ vitest:
+ specifier: ^4.0.17
+ version: 4.0.17(@types/node@22.19.3)(happy-dom@20.1.0)(jsdom@27.4.0)
packages:
+ '@acemir/cssom@0.9.31':
+ resolution: {integrity: sha512-ZnR3GSaH+/vJ0YlHau21FjfLYjMpYVIzTD8M8vIEQvIGxeOXyXdzCI140rrCY862p/C/BbzWsjc1dgnM9mkoTA==}
+
+ '@adobe/css-tools@4.4.4':
+ resolution: {integrity: sha512-Elp+iwUx5rN5+Y8xLt5/GRoG20WGoDCQ/1Fb+1LiGtvwbDavuSk0jhD/eZdckHAuzcDzccnkv+rEjyWfRx18gg==}
+
+ '@asamuzakjp/css-color@4.1.1':
+ resolution: {integrity: sha512-B0Hv6G3gWGMn0xKJ0txEi/jM5iFpT3MfDxmhZFb4W047GvytCf1DHQ1D69W3zHI4yWe2aTZAA0JnbMZ7Xc8DuQ==}
+
+ '@asamuzakjp/dom-selector@6.7.6':
+ resolution: {integrity: sha512-hBaJER6A9MpdG3WgdlOolHmbOYvSk46y7IQN/1+iqiCuUu6iWdQrs9DGKF8ocqsEqWujWf/V7b7vaDgiUmIvUg==}
+
+ '@asamuzakjp/nwsapi@2.3.9':
+ resolution: {integrity: sha512-n8GuYSrI9bF7FFZ/SjhwevlHc8xaVlb/7HmHelnc/PZXBD2ZR49NnN9sMMuDdEGPeeRQ5d0hqlSlEpgCX3Wl0Q==}
+
+ '@babel/code-frame@7.28.6':
+ resolution: {integrity: sha512-JYgintcMjRiCvS8mMECzaEn+m3PfoQiyqukOMCCVQtoJGYJw8j/8LBJEiqkHLkfwCcs74E3pbAUFNg7d9VNJ+Q==}
+ engines: {node: '>=6.9.0'}
+
+ '@babel/compat-data@7.28.6':
+ resolution: {integrity: sha512-2lfu57JtzctfIrcGMz992hyLlByuzgIk58+hhGCxjKZ3rWI82NnVLjXcaTqkI2NvlcvOskZaiZ5kjUALo3Lpxg==}
+ engines: {node: '>=6.9.0'}
+
+ '@babel/core@7.28.6':
+ resolution: {integrity: sha512-H3mcG6ZDLTlYfaSNi0iOKkigqMFvkTKlGUYlD8GW7nNOYRrevuA46iTypPyv+06V3fEmvvazfntkBU34L0azAw==}
+ engines: {node: '>=6.9.0'}
+
+ '@babel/generator@7.28.6':
+ resolution: {integrity: sha512-lOoVRwADj8hjf7al89tvQ2a1lf53Z+7tiXMgpZJL3maQPDxh0DgLMN62B2MKUOFcoodBHLMbDM6WAbKgNy5Suw==}
+ engines: {node: '>=6.9.0'}
+
+ '@babel/helper-compilation-targets@7.28.6':
+ resolution: {integrity: sha512-JYtls3hqi15fcx5GaSNL7SCTJ2MNmjrkHXg4FSpOA/grxK8KwyZ5bubHsCq8FXCkua6xhuaaBit+3b7+VZRfcA==}
+ engines: {node: '>=6.9.0'}
+
+ '@babel/helper-globals@7.28.0':
+ resolution: {integrity: sha512-+W6cISkXFa1jXsDEdYA8HeevQT/FULhxzR99pxphltZcVaugps53THCeiWA8SguxxpSp3gKPiuYfSWopkLQ4hw==}
+ engines: {node: '>=6.9.0'}
+
+ '@babel/helper-module-imports@7.28.6':
+ resolution: {integrity: sha512-l5XkZK7r7wa9LucGw9LwZyyCUscb4x37JWTPz7swwFE/0FMQAGpiWUZn8u9DzkSBWEcK25jmvubfpw2dnAMdbw==}
+ engines: {node: '>=6.9.0'}
+
+ '@babel/helper-module-transforms@7.28.6':
+ resolution: {integrity: sha512-67oXFAYr2cDLDVGLXTEABjdBJZ6drElUSI7WKp70NrpyISso3plG9SAGEF6y7zbha/wOzUByWWTJvEDVNIUGcA==}
+ engines: {node: '>=6.9.0'}
+ peerDependencies:
+ '@babel/core': ^7.0.0
+
+ '@babel/helper-plugin-utils@7.28.6':
+ resolution: {integrity: sha512-S9gzZ/bz83GRysI7gAD4wPT/AI3uCnY+9xn+Mx/KPs2JwHJIz1W8PZkg2cqyt3RNOBM8ejcXhV6y8Og7ly/Dug==}
+ engines: {node: '>=6.9.0'}
+
'@babel/helper-string-parser@7.27.1':
resolution: {integrity: sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==}
engines: {node: '>=6.9.0'}
@@ -41,15 +115,92 @@ packages:
resolution: {integrity: sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==}
engines: {node: '>=6.9.0'}
+ '@babel/helper-validator-option@7.27.1':
+ resolution: {integrity: sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg==}
+ engines: {node: '>=6.9.0'}
+
+ '@babel/helpers@7.28.6':
+ resolution: {integrity: sha512-xOBvwq86HHdB7WUDTfKfT/Vuxh7gElQ+Sfti2Cy6yIWNW05P8iUslOVcZ4/sKbE+/jQaukQAdz/gf3724kYdqw==}
+ engines: {node: '>=6.9.0'}
+
'@babel/parser@7.28.5':
resolution: {integrity: sha512-KKBU1VGYR7ORr3At5HAtUQ+TV3SzRCXmA/8OdDZiLDBIZxVyzXuztPjfLd3BV1PRAQGCMWWSHYhL0F8d5uHBDQ==}
engines: {node: '>=6.0.0'}
hasBin: true
+ '@babel/parser@7.28.6':
+ resolution: {integrity: sha512-TeR9zWR18BvbfPmGbLampPMW+uW1NZnJlRuuHso8i87QZNq2JRF9i6RgxRqtEq+wQGsS19NNTWr2duhnE49mfQ==}
+ engines: {node: '>=6.0.0'}
+ hasBin: true
+
+ '@babel/plugin-transform-react-jsx-self@7.27.1':
+ resolution: {integrity: sha512-6UzkCs+ejGdZ5mFFC/OCUrv028ab2fp1znZmCZjAOBKiBK2jXD1O+BPSfX8X2qjJ75fZBMSnQn3Rq2mrBJK2mw==}
+ engines: {node: '>=6.9.0'}
+ peerDependencies:
+ '@babel/core': ^7.0.0-0
+
+ '@babel/plugin-transform-react-jsx-source@7.27.1':
+ resolution: {integrity: sha512-zbwoTsBruTeKB9hSq73ha66iFeJHuaFkUbwvqElnygoNbj/jHRsSeokowZFN3CZ64IvEqcmmkVe89OPXc7ldAw==}
+ engines: {node: '>=6.9.0'}
+ peerDependencies:
+ '@babel/core': ^7.0.0-0
+
+ '@babel/runtime@7.28.6':
+ resolution: {integrity: sha512-05WQkdpL9COIMz4LjTxGpPNCdlpyimKppYNoJ5Di5EUObifl8t4tuLuUBBZEpoLYOmfvIWrsp9fCl0HoPRVTdA==}
+ engines: {node: '>=6.9.0'}
+
+ '@babel/template@7.28.6':
+ resolution: {integrity: sha512-YA6Ma2KsCdGb+WC6UpBVFJGXL58MDA6oyONbjyF/+5sBgxY/dwkhLogbMT2GXXyU84/IhRw/2D1Os1B/giz+BQ==}
+ engines: {node: '>=6.9.0'}
+
+ '@babel/traverse@7.28.6':
+ resolution: {integrity: sha512-fgWX62k02qtjqdSNTAGxmKYY/7FSL9WAS1o2Hu5+I5m9T0yxZzr4cnrfXQ/MX0rIifthCSs6FKTlzYbJcPtMNg==}
+ engines: {node: '>=6.9.0'}
+
'@babel/types@7.28.5':
resolution: {integrity: sha512-qQ5m48eI/MFLQ5PxQj4PFaprjyCTLI37ElWMmNs0K8Lk3dVeOdNpB3ks8jc7yM5CDmVC73eMVk/trk3fgmrUpA==}
engines: {node: '>=6.9.0'}
+ '@babel/types@7.28.6':
+ resolution: {integrity: sha512-0ZrskXVEHSWIqZM/sQZ4EV3jZJXRkio/WCxaqKZP1g//CEWEPSfeZFcms4XeKBCHU0ZKnIkdJeU/kF+eRp5lBg==}
+ engines: {node: '>=6.9.0'}
+
+ '@bcoe/v8-coverage@1.0.2':
+ resolution: {integrity: sha512-6zABk/ECA/QYSCQ1NGiVwwbQerUCZ+TQbp64Q3AgmfNvurHH0j8TtXa1qbShXA6qqkpAj4V5W8pP6mLe1mcMqA==}
+ engines: {node: '>=18'}
+
+ '@csstools/color-helpers@5.1.0':
+ resolution: {integrity: sha512-S11EXWJyy0Mz5SYvRmY8nJYTFFd1LCNV+7cXyAgQtOOuzb4EsgfqDufL+9esx72/eLhsRdGZwaldu/h+E4t4BA==}
+ engines: {node: '>=18'}
+
+ '@csstools/css-calc@2.1.4':
+ resolution: {integrity: sha512-3N8oaj+0juUw/1H3YwmDDJXCgTB1gKU6Hc/bB502u9zR0q2vd786XJH9QfrKIEgFlZmhZiq6epXl4rHqhzsIgQ==}
+ engines: {node: '>=18'}
+ peerDependencies:
+ '@csstools/css-parser-algorithms': ^3.0.5
+ '@csstools/css-tokenizer': ^3.0.4
+
+ '@csstools/css-color-parser@3.1.0':
+ resolution: {integrity: sha512-nbtKwh3a6xNVIp/VRuXV64yTKnb1IjTAEEh3irzS+HkKjAOYLTGNb9pmVNntZ8iVBHcWDA2Dof0QtPgFI1BaTA==}
+ engines: {node: '>=18'}
+ peerDependencies:
+ '@csstools/css-parser-algorithms': ^3.0.5
+ '@csstools/css-tokenizer': ^3.0.4
+
+ '@csstools/css-parser-algorithms@3.0.5':
+ resolution: {integrity: sha512-DaDeUkXZKjdGhgYaHNJTV9pV7Y9B3b644jCLs9Upc3VeNGg6LWARAT6O+Q+/COo+2gg/bM5rhpMAtf70WqfBdQ==}
+ engines: {node: '>=18'}
+ peerDependencies:
+ '@csstools/css-tokenizer': ^3.0.4
+
+ '@csstools/css-syntax-patches-for-csstree@1.0.25':
+ resolution: {integrity: sha512-g0Kw9W3vjx5BEBAF8c5Fm2NcB/Fs8jJXh85aXqwEXiL+tqtOut07TWgyaGzAAfTM+gKckrrncyeGEZPcaRgm2Q==}
+ engines: {node: '>=18'}
+
+ '@csstools/css-tokenizer@3.0.4':
+ resolution: {integrity: sha512-Vd/9EVDiu6PPJt9yAh6roZP6El1xHrdvIVGjyBsHR0RYwNHgL7FJPyIIW4fANJNG6FtyZfvlRPpFI4ZM/lubvw==}
+ engines: {node: '>=18'}
+
'@esbuild/aix-ppc64@0.25.12':
resolution: {integrity: sha512-Hhmwd6CInZ3dwpuGTF8fJG6yoWmsToE+vYgD4nytZVxcu1ulHpUQRAB1UJ8+N1Am3Mz4+xOByoQoSZf4D+CpkA==}
engines: {node: '>=18'}
@@ -206,6 +357,15 @@ packages:
cpu: [x64]
os: [win32]
+ '@exodus/bytes@1.8.0':
+ resolution: {integrity: sha512-8JPn18Bcp8Uo1T82gR8lh2guEOa5KKU/IEKvvdp0sgmi7coPBWf1Doi1EXsGZb2ehc8ym/StJCjffYV+ne7sXQ==}
+ engines: {node: ^20.19.0 || ^22.12.0 || >=24.0.0}
+ peerDependencies:
+ '@exodus/crypto': ^1.0.0-rc.4
+ peerDependenciesMeta:
+ '@exodus/crypto':
+ optional: true
+
'@isaacs/balanced-match@4.0.1':
resolution: {integrity: sha512-yzMTt9lEb8Gv7zRioUilSglI0c0smZ9k5D65677DLWLtWJaXIS3CqcGyUFByYKlnUj6TkjLVs54fBl6+TiGQDQ==}
engines: {node: 20 || >=22}
@@ -214,9 +374,22 @@ packages:
resolution: {integrity: sha512-ZT55BDLV0yv0RBm2czMiZ+SqCGO7AvmOM3G/w2xhVPH+te0aKgFjmBvGlL1dH+ql2tgGO3MVrbb3jCKyvpgnxA==}
engines: {node: 20 || >=22}
+ '@jridgewell/gen-mapping@0.3.13':
+ resolution: {integrity: sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==}
+
+ '@jridgewell/remapping@2.3.5':
+ resolution: {integrity: sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==}
+
+ '@jridgewell/resolve-uri@3.1.2':
+ resolution: {integrity: sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==}
+ engines: {node: '>=6.0.0'}
+
'@jridgewell/sourcemap-codec@1.5.5':
resolution: {integrity: sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==}
+ '@jridgewell/trace-mapping@0.3.31':
+ resolution: {integrity: sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==}
+
'@microsoft/api-extractor-model@7.32.2':
resolution: {integrity: sha512-Ussc25rAalc+4JJs9HNQE7TuO9y6jpYQX9nWD1DhqUzYPBr3Lr7O9intf+ZY8kD5HnIqeIRJX7ccCT0QyBy2Ww==}
@@ -230,6 +403,9 @@ packages:
'@microsoft/tsdoc@0.16.0':
resolution: {integrity: sha512-xgAyonlVVS+q7Vc7qLW0UrJU7rSFcETRWsqdXZtjzRU8dF+6CkozTK4V4y1LwOX7j8r/vHphjDeMeGI4tNGeGA==}
+ '@rolldown/pluginutils@1.0.0-beta.53':
+ resolution: {integrity: sha512-vENRlFU4YbrwVqNDZ7fLvy+JR1CRkyr01jhSiDpE1u6py3OMzQfztQU2jxykW3ALNxO4kSlqIDeYyD0Y9RcQeQ==}
+
'@rollup/pluginutils@5.3.0':
resolution: {integrity: sha512-5EdhGZtnu3V88ces7s53hhfK5KSASnJZv8Lulpc04cWO3REESroJXg73DFsOmgbU2BhwV0E20bu2IDZb3VKW4Q==}
engines: {node: '>=14.0.0'}
@@ -394,18 +570,118 @@ packages:
'@rushstack/ts-command-line@5.1.5':
resolution: {integrity: sha512-YmrFTFUdHXblYSa+Xc9OO9FsL/XFcckZy0ycQ6q7VSBsVs5P0uD9vcges5Q9vctGlVdu27w+Ct6IuJ458V0cTQ==}
+ '@standard-schema/spec@1.1.0':
+ resolution: {integrity: sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w==}
+
+ '@testing-library/dom@10.4.1':
+ resolution: {integrity: sha512-o4PXJQidqJl82ckFaXUeoAW+XysPLauYI43Abki5hABd853iMhitooc6znOnczgbTYmEP6U6/y1ZyKAIsvMKGg==}
+ engines: {node: '>=18'}
+
+ '@testing-library/jest-dom@6.9.1':
+ resolution: {integrity: sha512-zIcONa+hVtVSSep9UT3jZ5rizo2BsxgyDYU7WFD5eICBE7no3881HGeb/QkGfsJs6JTkY1aQhT7rIPC7e+0nnA==}
+ engines: {node: '>=14', npm: '>=6', yarn: '>=1'}
+
+ '@testing-library/react@16.3.1':
+ resolution: {integrity: sha512-gr4KtAWqIOQoucWYD/f6ki+j5chXfcPc74Col/6poTyqTmn7zRmodWahWRCp8tYd+GMqBonw6hstNzqjbs6gjw==}
+ engines: {node: '>=18'}
+ peerDependencies:
+ '@testing-library/dom': ^10.0.0
+ '@types/react': ^18.0.0 || ^19.0.0
+ '@types/react-dom': ^18.0.0 || ^19.0.0
+ react: ^18.0.0 || ^19.0.0
+ react-dom: ^18.0.0 || ^19.0.0
+ peerDependenciesMeta:
+ '@types/react':
+ optional: true
+ '@types/react-dom':
+ optional: true
+
'@types/argparse@1.0.38':
resolution: {integrity: sha512-ebDJ9b0e702Yr7pWgB0jzm+CX4Srzz8RcXtLJDJB+BSccqMa36uyH/zUsSYao5+BD1ytv3k3rPYCq4mAE1hsXA==}
+ '@types/aria-query@5.0.4':
+ resolution: {integrity: sha512-rfT93uj5s0PRL7EzccGMs3brplhcrghnDoV26NqKhCAS1hVo+WdNsPvE/yb6ilfr5hi2MEk6d5EWJTKdxg8jVw==}
+
+ '@types/babel__core@7.20.5':
+ resolution: {integrity: sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==}
+
+ '@types/babel__generator@7.27.0':
+ resolution: {integrity: sha512-ufFd2Xi92OAVPYsy+P4n7/U7e68fex0+Ee8gSG9KX7eo084CWiQ4sdxktvdl0bOPupXtVJPY19zk6EwWqUQ8lg==}
+
+ '@types/babel__template@7.4.4':
+ resolution: {integrity: sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A==}
+
+ '@types/babel__traverse@7.28.0':
+ resolution: {integrity: sha512-8PvcXf70gTDZBgt9ptxJ8elBeBjcLOAcOtoO/mPJjtji1+CdGbHgm77om1GrsPxsiE+uXIpNSK64UYaIwQXd4Q==}
+
+ '@types/chai@5.2.3':
+ resolution: {integrity: sha512-Mw558oeA9fFbv65/y4mHtXDs9bPnFMZAL/jxdPFUpOHHIXX91mcgEHbS5Lahr+pwZFR8A7GQleRWeI6cGFC2UA==}
+
+ '@types/deep-eql@4.0.2':
+ resolution: {integrity: sha512-c9h9dVVMigMPc4bwTvC5dxqtqJZwQPePsWjPlpSOnojbor6pGqdk541lfA7AqFQr5pB1BRdq0juY9db81BwyFw==}
+
'@types/estree@1.0.8':
resolution: {integrity: sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==}
+ '@types/node@20.19.29':
+ resolution: {integrity: sha512-YrT9ArrGaHForBaCNwFjoqJWmn8G1Pr7+BH/vwyLHciA9qT/wSiuOhxGCT50JA5xLvFBd6PIiGkE3afxcPE1nw==}
+
'@types/node@22.19.3':
resolution: {integrity: sha512-1N9SBnWYOJTrNZCdh/yJE+t910Y128BoyY+zBLWhL3r0TYzlTmFdXrPwHL9DyFZmlEXNQQolTZh3KHV31QDhyA==}
'@types/react@19.2.7':
resolution: {integrity: sha512-MWtvHrGZLFttgeEj28VXHxpmwYbor/ATPYbBfSFZEIRK0ecCFLl2Qo55z52Hss+UV9CRN7trSeq1zbgx7YDWWg==}
+ '@types/whatwg-mimetype@3.0.2':
+ resolution: {integrity: sha512-c2AKvDT8ToxLIOUlN51gTiHXflsfIFisS4pO7pDPoKouJCESkhZnEy623gwP9laCy5lnLDAw1vAzu2vM2YLOrA==}
+
+ '@types/ws@8.18.1':
+ resolution: {integrity: sha512-ThVF6DCVhA8kUGy+aazFQ4kXQ7E1Ty7A3ypFOe0IcJV8O/M511G99AW24irKrW56Wt44yG9+ij8FaqoBGkuBXg==}
+
+ '@vitejs/plugin-react@5.1.2':
+ resolution: {integrity: sha512-EcA07pHJouywpzsoTUqNh5NwGayl2PPVEJKUSinGGSxFGYn+shYbqMGBg6FXDqgXum9Ou/ecb+411ssw8HImJQ==}
+ engines: {node: ^20.19.0 || >=22.12.0}
+ peerDependencies:
+ vite: ^4.2.0 || ^5.0.0 || ^6.0.0 || ^7.0.0
+
+ '@vitest/coverage-v8@4.0.17':
+ resolution: {integrity: sha512-/6zU2FLGg0jsd+ePZcwHRy3+WpNTBBhDY56P4JTRqUN/Dp6CvOEa9HrikcQ4KfV2b2kAHUFB4dl1SuocWXSFEw==}
+ peerDependencies:
+ '@vitest/browser': 4.0.17
+ vitest: 4.0.17
+ peerDependenciesMeta:
+ '@vitest/browser':
+ optional: true
+
+ '@vitest/expect@4.0.17':
+ resolution: {integrity: sha512-mEoqP3RqhKlbmUmntNDDCJeTDavDR+fVYkSOw8qRwJFaW/0/5zA9zFeTrHqNtcmwh6j26yMmwx2PqUDPzt5ZAQ==}
+
+ '@vitest/mocker@4.0.17':
+ resolution: {integrity: sha512-+ZtQhLA3lDh1tI2wxe3yMsGzbp7uuJSWBM1iTIKCbppWTSBN09PUC+L+fyNlQApQoR+Ps8twt2pbSSXg2fQVEQ==}
+ peerDependencies:
+ msw: ^2.4.9
+ vite: ^6.0.0 || ^7.0.0-0
+ peerDependenciesMeta:
+ msw:
+ optional: true
+ vite:
+ optional: true
+
+ '@vitest/pretty-format@4.0.17':
+ resolution: {integrity: sha512-Ah3VAYmjcEdHg6+MwFE17qyLqBHZ+ni2ScKCiW2XrlSBV4H3Z7vYfPfz7CWQ33gyu76oc0Ai36+kgLU3rfF4nw==}
+
+ '@vitest/runner@4.0.17':
+ resolution: {integrity: sha512-JmuQyf8aMWoo/LmNFppdpkfRVHJcsgzkbCA+/Bk7VfNH7RE6Ut2qxegeyx2j3ojtJtKIbIGy3h+KxGfYfk28YQ==}
+
+ '@vitest/snapshot@4.0.17':
+ resolution: {integrity: sha512-npPelD7oyL+YQM2gbIYvlavlMVWUfNNGZPcu0aEUQXt7FXTuqhmgiYupPnAanhKvyP6Srs2pIbWo30K0RbDtRQ==}
+
+ '@vitest/spy@4.0.17':
+ resolution: {integrity: sha512-I1bQo8QaP6tZlTomQNWKJE6ym4SHf3oLS7ceNjozxxgzavRAgZDc06T7kD8gb9bXKEgcLNt00Z+kZO6KaJ62Ew==}
+
+ '@vitest/utils@4.0.17':
+ resolution: {integrity: sha512-RG6iy+IzQpa9SB8HAFHJ9Y+pTzI+h8553MrciN9eC6TFBErqrQaTas4vG+MVj8S4uKk8uTT2p0vgZPnTdxd96w==}
+
'@volar/language-core@2.4.27':
resolution: {integrity: sha512-DjmjBWZ4tJKxfNC1F6HyYERNHPYS7L7OPFyCrestykNdUZMFYzI9WTyvwPcaNaHlrEUwESHYsfEw3isInncZxQ==}
@@ -440,6 +716,10 @@ packages:
engines: {node: '>=0.4.0'}
hasBin: true
+ agent-base@7.1.4:
+ resolution: {integrity: sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ==}
+ engines: {node: '>= 14'}
+
ajv-draft-04@1.0.0:
resolution: {integrity: sha512-mv00Te6nmYbRp5DCwclxtt7yV/joXJPGS7nM+97GdxvuttCOfgI3K4U25zboyeX0O+myI8ERluxQe5wljMmVIw==}
peerDependencies:
@@ -465,15 +745,56 @@ packages:
alien-signals@0.4.14:
resolution: {integrity: sha512-itUAVzhczTmP2U5yX67xVpsbbOiquusbWVyA9N+sy6+r6YVbFkahXvNCeEPWEOMhwDYwbVbGHFkVL03N9I5g+Q==}
+ ansi-regex@5.0.1:
+ resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==}
+ engines: {node: '>=8'}
+
+ ansi-styles@5.2.0:
+ resolution: {integrity: sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==}
+ engines: {node: '>=10'}
+
argparse@1.0.10:
resolution: {integrity: sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==}
+ aria-query@5.3.0:
+ resolution: {integrity: sha512-b0P0sZPKtyu8HkeRAfCq0IfURZK+SuwMjY1UXGBU27wpAiTwQAIlq56IbIO+ytk/JjS1fMR14ee5WBBfKi5J6A==}
+
+ aria-query@5.3.2:
+ resolution: {integrity: sha512-COROpnaoap1E2F000S62r6A60uHZnmlvomhfyT2DlTcrY1OrBKn2UhH7qn5wTC9zMvD0AY7csdPSNwKP+7WiQw==}
+ engines: {node: '>= 0.4'}
+
+ assertion-error@2.0.1:
+ resolution: {integrity: sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==}
+ engines: {node: '>=12'}
+
+ ast-v8-to-istanbul@0.3.10:
+ resolution: {integrity: sha512-p4K7vMz2ZSk3wN8l5o3y2bJAoZXT3VuJI5OLTATY/01CYWumWvwkUw0SqDBnNq6IiTO3qDa1eSQDibAV8g7XOQ==}
+
balanced-match@1.0.2:
resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==}
+ baseline-browser-mapping@2.9.14:
+ resolution: {integrity: sha512-B0xUquLkiGLgHhpPBqvl7GWegWBUNuujQ6kXd/r1U38ElPT6Ok8KZ8e+FpUGEc2ZoRQUzq/aUnaKFc/svWUGSg==}
+ hasBin: true
+
+ bidi-js@1.0.3:
+ resolution: {integrity: sha512-RKshQI1R3YQ+n9YJz2QQ147P66ELpa1FQEg20Dk8oW9t2KgLbpDLLp9aGZ7y8WHSshDknG0bknqGw5/tyCs5tw==}
+
brace-expansion@2.0.2:
resolution: {integrity: sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==}
+ browserslist@4.28.1:
+ resolution: {integrity: sha512-ZC5Bd0LgJXgwGqUknZY/vkUQ04r8NXnJZ3yYi4vDmSiZmC/pdSN0NbNRPxZpbtO4uAfDUAFffO8IZoM3Gj8IkA==}
+ engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7}
+ hasBin: true
+
+ caniuse-lite@1.0.30001764:
+ resolution: {integrity: sha512-9JGuzl2M+vPL+pz70gtMF9sHdMFbY9FJaQBi186cHKH3pSzDvzoUJUPV6fqiKIMyXbud9ZLg4F3Yza1vJ1+93g==}
+
+ chai@6.2.2:
+ resolution: {integrity: sha512-NUPRluOfOiTKBKvWPtSD4PhFvWCqOi0BGStNWs57X9js7XGTprSmFoz5F0tWhR4WPjNeR9jXqdC7/UpSJTnlRg==}
+ engines: {node: '>=18'}
+
compare-versions@6.1.1:
resolution: {integrity: sha512-4hm4VPpIecmlg59CHXnRDnqGplJFrbLG4aFEl5vl6cK1u76ws3LLvX7ikFnTDl5vo39sjWD6AaDPYodJp/NNHg==}
@@ -483,9 +804,27 @@ packages:
confbox@0.2.2:
resolution: {integrity: sha512-1NB+BKqhtNipMsov4xI/NnhCKp9XG9NamYp5PVm9klAT0fsrNPjaFICsCFhNhwZJKNh7zB/3q8qXz0E9oaMNtQ==}
+ convert-source-map@2.0.0:
+ resolution: {integrity: sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==}
+
+ css-tree@3.1.0:
+ resolution: {integrity: sha512-0eW44TGN5SQXU1mWSkKwFstI/22X2bG1nYzZTYMAWjylYURhse752YgbE4Cx46AC+bAvI+/dYTPRk1LqSUnu6w==}
+ engines: {node: ^10 || ^12.20.0 || ^14.13.0 || >=15.0.0}
+
+ css.escape@1.5.1:
+ resolution: {integrity: sha512-YUifsXXuknHlUsmlgyY0PKzgPOr7/FjCePfHNt0jxm83wHZi44VDMQ7/fGNkjY3/jV1MC+1CmZbaHzugyeRtpg==}
+
+ cssstyle@5.3.7:
+ resolution: {integrity: sha512-7D2EPVltRrsTkhpQmksIu+LxeWAIEk6wRDMJ1qljlv+CKHJM+cJLlfhWIzNA44eAsHXSNe3+vO6DW1yCYx8SuQ==}
+ engines: {node: '>=20'}
+
csstype@3.2.3:
resolution: {integrity: sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==}
+ data-urls@6.0.0:
+ resolution: {integrity: sha512-BnBS08aLUM+DKamupXs3w2tJJoqU+AkaE/+6vQxi/G/DPmIZFJJp9Dkb1kM03AZx8ADehDUZgsNxju3mPXZYIA==}
+ engines: {node: '>=20'}
+
de-indent@1.0.2:
resolution: {integrity: sha512-e/1zu3xH5MQryN2zdVaF0OrdNLUbvWxzMbi+iNA6Bky7l1RoP8a2fIbRocyHclXt/arDrrR6lL3TqFD9pMQTsg==}
@@ -498,22 +837,56 @@ packages:
supports-color:
optional: true
+ decimal.js@10.6.0:
+ resolution: {integrity: sha512-YpgQiITW3JXGntzdUmyUR1V812Hn8T1YVXhCu+wO3OpS4eU9l4YdD3qjyiKdV6mvV29zapkMeD390UVEf2lkUg==}
+
+ dequal@2.0.3:
+ resolution: {integrity: sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==}
+ engines: {node: '>=6'}
+
diff@8.0.2:
resolution: {integrity: sha512-sSuxWU5j5SR9QQji/o2qMvqRNYRDOcBTgsJ/DeCf4iSN4gW+gNMXM7wFIP+fdXZxoNiAnHUTGjCr+TSWXdRDKg==}
engines: {node: '>=0.3.1'}
+ dom-accessibility-api@0.5.16:
+ resolution: {integrity: sha512-X7BJ2yElsnOJ30pZF4uIIDfBEVgF4XEBxL9Bxhy6dnrm5hkzqmsWHGTiHqRiITNhMyFLyAiWndIJP7Z1NTteDg==}
+
+ dom-accessibility-api@0.6.3:
+ resolution: {integrity: sha512-7ZgogeTnjuHbo+ct10G9Ffp0mif17idi0IyWNVA/wcwcm7NPOD/WEHVP3n7n3MhXqxoIYm8d6MuZohYWIZ4T3w==}
+
+ electron-to-chromium@1.5.267:
+ resolution: {integrity: sha512-0Drusm6MVRXSOJpGbaSVgcQsuB4hEkMpHXaVstcPmhu5LIedxs1xNK/nIxmQIU/RPC0+1/o0AVZfBTkTNJOdUw==}
+
+ entities@6.0.1:
+ resolution: {integrity: sha512-aN97NXWF6AWBTahfVOIrB/NShkzi5H7F9r1s9mD3cDj4Ko5f2qhhVoYMibXF7GlLveb/D2ioWay8lxI97Ven3g==}
+ engines: {node: '>=0.12'}
+
entities@7.0.0:
resolution: {integrity: sha512-FDWG5cmEYf2Z00IkYRhbFrwIwvdFKH07uV8dvNy0omp/Qb1xcyCWp2UDtcwJF4QZZvk0sLudP6/hAu42TaqVhQ==}
engines: {node: '>=0.12'}
+ es-module-lexer@1.7.0:
+ resolution: {integrity: sha512-jEQoCwk8hyb2AZziIOLhDqpm5+2ww5uIE6lkO/6jcOCusfk6LhMHpXXfBLXTZ7Ydyt0j4VoUQv6uGNYbdW+kBA==}
+
esbuild@0.25.12:
resolution: {integrity: sha512-bbPBYYrtZbkt6Os6FiTLCTFxvq4tt3JKall1vRwshA3fdVztsLAatFaZobhkBC8/BrPetoa0oksYoKXoG4ryJg==}
engines: {node: '>=18'}
hasBin: true
+ escalade@3.2.0:
+ resolution: {integrity: sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==}
+ engines: {node: '>=6'}
+
estree-walker@2.0.2:
resolution: {integrity: sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==}
+ estree-walker@3.0.3:
+ resolution: {integrity: sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==}
+
+ expect-type@1.3.0:
+ resolution: {integrity: sha512-knvyeauYhqjOYvQ66MznSMs83wmHrCycNEN6Ao+2AeYEfxUIkuiVxdEa1qlGEPK+We3n0THiDciYSsCcgW/DoA==}
+ engines: {node: '>=12.0.0'}
+
exsolve@1.0.8:
resolution: {integrity: sha512-LmDxfWXwcTArk8fUEnOfSZpHOJ6zOMUJKOtFLFqJLoKJetuQG874Uc7/Kki7zFLzYybmZhp1M7+98pfMqeX8yA==}
@@ -541,9 +914,17 @@ packages:
function-bind@1.1.2:
resolution: {integrity: sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==}
+ gensync@1.0.0-beta.2:
+ resolution: {integrity: sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==}
+ engines: {node: '>=6.9.0'}
+
graceful-fs@4.2.11:
resolution: {integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==}
+ happy-dom@20.1.0:
+ resolution: {integrity: sha512-ebvqjBqzenBk2LjzNEAzoj7yhw7rW/R2/wVevMu6Mrq3MXtcI/RUz4+ozpcOcqVLEWPqLfg2v9EAU7fFXZUUJw==}
+ engines: {node: '>=20.0.0'}
+
has-flag@4.0.0:
resolution: {integrity: sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==}
engines: {node: '>=8'}
@@ -556,20 +937,79 @@ packages:
resolution: {integrity: sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==}
hasBin: true
+ html-encoding-sniffer@6.0.0:
+ resolution: {integrity: sha512-CV9TW3Y3f8/wT0BRFc1/KAVQ3TUHiXmaAb6VW9vtiMFf7SLoMd1PdAc4W3KFOFETBJUb90KatHqlsZMWV+R9Gg==}
+ engines: {node: ^20.19.0 || ^22.12.0 || >=24.0.0}
+
+ html-escaper@2.0.2:
+ resolution: {integrity: sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==}
+
+ http-proxy-agent@7.0.2:
+ resolution: {integrity: sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig==}
+ engines: {node: '>= 14'}
+
+ https-proxy-agent@7.0.6:
+ resolution: {integrity: sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==}
+ engines: {node: '>= 14'}
+
import-lazy@4.0.0:
resolution: {integrity: sha512-rKtvo6a868b5Hu3heneU+L4yEQ4jYKLtjpnPeUdK7h0yzXGmyBTypknlkCvHFBqfX9YlorEiMM6Dnq/5atfHkw==}
engines: {node: '>=8'}
+ indent-string@4.0.0:
+ resolution: {integrity: sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==}
+ engines: {node: '>=8'}
+
is-core-module@2.16.1:
resolution: {integrity: sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==}
engines: {node: '>= 0.4'}
+ is-potential-custom-element-name@1.0.1:
+ resolution: {integrity: sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ==}
+
+ istanbul-lib-coverage@3.2.2:
+ resolution: {integrity: sha512-O8dpsF+r0WV/8MNRKfnmrtCWhuKjxrq2w+jpzBL5UZKTi2LeVWnWOmWRxFlesJONmc+wLAGvKQZEOanko0LFTg==}
+ engines: {node: '>=8'}
+
+ istanbul-lib-report@3.0.1:
+ resolution: {integrity: sha512-GCfE1mtsHGOELCU8e/Z7YWzpmybrx/+dSTfLrvY8qRmaY6zXTKWn6WQIjaAFw069icm6GVMNkgu0NzI4iPZUNw==}
+ engines: {node: '>=10'}
+
+ istanbul-reports@3.2.0:
+ resolution: {integrity: sha512-HGYWWS/ehqTV3xN10i23tkPkpH46MLCIMFNCaaKNavAXTF1RkqxawEPtnjnGZ6XKSInBKkiOA5BKS+aZiY3AvA==}
+ engines: {node: '>=8'}
+
jju@1.4.0:
resolution: {integrity: sha512-8wb9Yw966OSxApiCt0K3yNJL8pnNeIv+OEq2YMidz4FKP6nonSRoOXc80iXY4JaN2FC11B9qsNmDsm+ZOfMROA==}
+ js-tokens@4.0.0:
+ resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==}
+
+ js-tokens@9.0.1:
+ resolution: {integrity: sha512-mxa9E9ITFOt0ban3j6L5MpjwegGz6lBQmM1IJkWeBZGcMxto50+eWdjC/52xDbS2vy0k7vIMK0Fe2wfL9OQSpQ==}
+
+ jsdom@27.4.0:
+ resolution: {integrity: sha512-mjzqwWRD9Y1J1KUi7W97Gja1bwOOM5Ug0EZ6UDK3xS7j7mndrkwozHtSblfomlzyB4NepioNt+B2sOSzczVgtQ==}
+ engines: {node: ^20.19.0 || ^22.12.0 || >=24.0.0}
+ peerDependencies:
+ canvas: ^3.0.0
+ peerDependenciesMeta:
+ canvas:
+ optional: true
+
+ jsesc@3.1.0:
+ resolution: {integrity: sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==}
+ engines: {node: '>=6'}
+ hasBin: true
+
json-schema-traverse@1.0.0:
resolution: {integrity: sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==}
+ json5@2.2.3:
+ resolution: {integrity: sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==}
+ engines: {node: '>=6'}
+ hasBin: true
+
jsonfile@6.2.0:
resolution: {integrity: sha512-FGuPw30AdOIUTRMC2OMRtQV+jkVj2cfPqSeWXv1NEAJ1qZ5zb1X6z1mFhbfOB/iy3ssJCD+3KuZ8r8C3uVFlAg==}
@@ -583,13 +1023,38 @@ packages:
lodash@4.17.21:
resolution: {integrity: sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==}
+ lru-cache@11.2.4:
+ resolution: {integrity: sha512-B5Y16Jr9LB9dHVkh6ZevG+vAbOsNOYCX+sXvFWFu7B3Iz5mijW3zdbMyhsh8ANd2mSWBYdJgnqi+mL7/LrOPYg==}
+ engines: {node: 20 || >=22}
+
+ lru-cache@5.1.1:
+ resolution: {integrity: sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==}
+
lru-cache@6.0.0:
resolution: {integrity: sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==}
engines: {node: '>=10'}
+ lz-string@1.5.0:
+ resolution: {integrity: sha512-h5bgJWpxJNswbU7qCrV0tIKQCaS3blPDrqKWx+QxzuzL1zGUzij9XCWLrSLsJPu5t+eWA/ycetzYAO5IOMcWAQ==}
+ hasBin: true
+
magic-string@0.30.21:
resolution: {integrity: sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==}
+ magicast@0.5.1:
+ resolution: {integrity: sha512-xrHS24IxaLrvuo613F719wvOIv9xPHFWQHuvGUBmPnCA/3MQxKI3b+r7n1jAoDHmsbC5bRhTZYR77invLAxVnw==}
+
+ make-dir@4.0.0:
+ resolution: {integrity: sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw==}
+ engines: {node: '>=10'}
+
+ mdn-data@2.12.2:
+ resolution: {integrity: sha512-IEn+pegP1aManZuckezWCO+XZQDplx1366JoVhTpMpBB1sPey/SbveZQUosKiKiGYjg1wH4pMlNgXbCiYgihQA==}
+
+ min-indent@1.0.1:
+ resolution: {integrity: sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg==}
+ engines: {node: '>=4'}
+
minimatch@10.0.3:
resolution: {integrity: sha512-IPZ167aShDZZUMdRk66cyQAW3qr0WzbHkPdMYa8bzZhlHhO3jALbKdxcaak7W9FfT2rZNpQuUu4Od7ILEpXSaw==}
engines: {node: 20 || >=22}
@@ -612,6 +1077,15 @@ packages:
engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1}
hasBin: true
+ node-releases@2.0.27:
+ resolution: {integrity: sha512-nmh3lCkYZ3grZvqcCH+fjmQ7X+H0OeZgP40OierEaAptX4XofMh5kwNbWh7lBduUzCcV/8kZ+NDLCwm2iorIlA==}
+
+ obug@2.1.1:
+ resolution: {integrity: sha512-uTqF9MuPraAQ+IsnPf366RG4cP9RtUi7MLO1N3KEc+wb0a6yKpeL0lmk2IB1jY5KHPAlTc6T/JRdC/YqxHNwkQ==}
+
+ parse5@8.0.0:
+ resolution: {integrity: sha512-9m4m5GSgXjL4AjumKzq1Fgfp3Z8rsvjRNbnkVwfu2ImRqE5D0LnY2QfDen18FSY9C573YU5XxSapdHZTZ2WolA==}
+
path-browserify@1.0.1:
resolution: {integrity: sha512-b7uo2UCUOYZcnF/3ID0lulOJi/bafxa1xPe7ZPsammBSpjSWQkjNxlt635YGS2MiR9GjvuXCtz2emr3jbsz98g==}
@@ -643,6 +1117,10 @@ packages:
engines: {node: '>=14'}
hasBin: true
+ pretty-format@27.5.1:
+ resolution: {integrity: sha512-Qb1gy5OrP5+zDf2Bvnzdl3jsTf1qXVMazbvCoKhtKqVs4/YK4ozX4gKQJJVyNe+cajNPn0KoC0MC3FUmaHWEmQ==}
+ engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0}
+
punycode@2.3.1:
resolution: {integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==}
engines: {node: '>=6'}
@@ -650,10 +1128,26 @@ packages:
quansync@0.2.11:
resolution: {integrity: sha512-AifT7QEbW9Nri4tAwR5M/uzpBuqfZf+zwaEM/QkzEjj7NBuFD2rBuy0K3dE+8wltbezDV7JMA0WfnCPYRSYbXA==}
+ react-dom@19.2.3:
+ resolution: {integrity: sha512-yELu4WmLPw5Mr/lmeEpox5rw3RETacE++JgHqQzd2dg+YbJuat3jH4ingc+WPZhxaoFzdv9y33G+F7Nl5O0GBg==}
+ peerDependencies:
+ react: ^19.2.3
+
+ react-is@17.0.2:
+ resolution: {integrity: sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==}
+
+ react-refresh@0.18.0:
+ resolution: {integrity: sha512-QgT5//D3jfjJb6Gsjxv0Slpj23ip+HtOpnNgnb2S5zU3CB26G/IDPGoy4RJB42wzFE46DRsstbW6tKHoKbhAxw==}
+ engines: {node: '>=0.10.0'}
+
react@19.2.3:
resolution: {integrity: sha512-Ku/hhYbVjOQnXDZFv2+RibmLFGwFdeeKHFcOTlrt7xplBnya5OGn/hIRDsqDiSUcfORsDC7MPxwork8jBwsIWA==}
engines: {node: '>=0.10.0'}
+ redent@3.0.0:
+ resolution: {integrity: sha512-6tDA8g98We0zd0GvVeMT9arEOnTw9qM03L9cJXaCjrip1OO764RDBLBfrB4cwzNGDj5OA5ioymC9GkizgWJDUg==}
+ engines: {node: '>=8'}
+
require-from-string@2.0.2:
resolution: {integrity: sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==}
engines: {node: '>=0.10.0'}
@@ -668,11 +1162,25 @@ packages:
engines: {node: '>=18.0.0', npm: '>=8.0.0'}
hasBin: true
+ saxes@6.0.0:
+ resolution: {integrity: sha512-xAg7SOnEhrm5zI3puOOKyy1OMcMlIJZYNJY7xLBwSze0UjhPLnWfj2GF2EpT0jmzaJKIWKHLsaSSajf35bcYnA==}
+ engines: {node: '>=v12.22.7'}
+
+ scheduler@0.27.0:
+ resolution: {integrity: sha512-eNv+WrVbKu1f3vbYJT/xtiF5syA5HPIMtf9IgY/nKg0sWqzAUEvqY/xm7OcZc/qafLx/iO9FgOmeSAp4v5ti/Q==}
+
+ semver@6.3.1:
+ resolution: {integrity: sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==}
+ hasBin: true
+
semver@7.5.4:
resolution: {integrity: sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==}
engines: {node: '>=10'}
hasBin: true
+ siginfo@2.0.0:
+ resolution: {integrity: sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==}
+
source-map-js@1.2.1:
resolution: {integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==}
engines: {node: '>=0.10.0'}
@@ -684,14 +1192,28 @@ packages:
sprintf-js@1.0.3:
resolution: {integrity: sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==}
+ stackback@0.0.2:
+ resolution: {integrity: sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==}
+
+ std-env@3.10.0:
+ resolution: {integrity: sha512-5GS12FdOZNliM5mAOxFRg7Ir0pWz8MdpYm6AY6VPkGpbA7ZzmbzNcBJQ0GPvvyWgcY7QAhCgf9Uy89I03faLkg==}
+
string-argv@0.3.2:
resolution: {integrity: sha512-aqD2Q0144Z+/RqG52NeHEkZauTAUWJO8c6yTftGJKO3Tja5tUgIfmIl6kExvhtxSDP7fXB6DvzkfMpCd/F3G+Q==}
engines: {node: '>=0.6.19'}
+ strip-indent@3.0.0:
+ resolution: {integrity: sha512-laJTa3Jb+VQpaC6DseHhF7dXVqHTfJPCRDaEbid/drOhgitgYku/letMUqOXFoWV0zIIUbjpdH2t+tYj4bQMRQ==}
+ engines: {node: '>=8'}
+
strip-json-comments@3.1.1:
resolution: {integrity: sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==}
engines: {node: '>=8'}
+ supports-color@7.2.0:
+ resolution: {integrity: sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==}
+ engines: {node: '>=8'}
+
supports-color@8.1.1:
resolution: {integrity: sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==}
engines: {node: '>=10'}
@@ -700,10 +1222,39 @@ packages:
resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==}
engines: {node: '>= 0.4'}
+ symbol-tree@3.2.4:
+ resolution: {integrity: sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==}
+
+ tinybench@2.9.0:
+ resolution: {integrity: sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg==}
+
+ tinyexec@1.0.2:
+ resolution: {integrity: sha512-W/KYk+NFhkmsYpuHq5JykngiOCnxeVL8v8dFnqxSD8qEEdRfXk1SDM6JzNqcERbcGYj9tMrDQBYV9cjgnunFIg==}
+ engines: {node: '>=18'}
+
tinyglobby@0.2.15:
resolution: {integrity: sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==}
engines: {node: '>=12.0.0'}
+ tinyrainbow@3.0.3:
+ resolution: {integrity: sha512-PSkbLUoxOFRzJYjjxHJt9xro7D+iilgMX/C9lawzVuYiIdcihh9DXmVibBe8lmcFrRi/VzlPjBxbN7rH24q8/Q==}
+ engines: {node: '>=14.0.0'}
+
+ tldts-core@7.0.19:
+ resolution: {integrity: sha512-lJX2dEWx0SGH4O6p+7FPwYmJ/bu1JbcGJ8RLaG9b7liIgZ85itUVEPbMtWRVrde/0fnDPEPHW10ZsKW3kVsE9A==}
+
+ tldts@7.0.19:
+ resolution: {integrity: sha512-8PWx8tvC4jDB39BQw1m4x8y5MH1BcQ5xHeL2n7UVFulMPH/3Q0uiamahFJ3lXA0zO2SUyRXuVVbWSDmstlt9YA==}
+ hasBin: true
+
+ tough-cookie@6.0.0:
+ resolution: {integrity: sha512-kXuRi1mtaKMrsLUxz3sQYvVl37B0Ns6MzfrtV5DvJceE9bPyspOqk9xxv7XbZWcfLWbFmm997vl83qUWVJA64w==}
+ engines: {node: '>=16'}
+
+ tr46@6.0.0:
+ resolution: {integrity: sha512-bLVMLPtstlZ4iMQHpFHTR7GAGj2jxi8Dg0s2h2MafAE4uSWF98FC/3MomU51iQAMf8/qDUbKWf5GxuvvVcXEhw==}
+ engines: {node: '>=20'}
+
typescript@5.8.2:
resolution: {integrity: sha512-aJn6wq13/afZp/jT9QZmwEjDqqvSGp1VT5GVg+f/t6/oVyrgXM6BY1h9BRh/O5p3PlUPAe+WuiEZOmb/49RqoQ==}
engines: {node: '>=14.17'}
@@ -724,6 +1275,12 @@ packages:
resolution: {integrity: sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==}
engines: {node: '>= 10.0.0'}
+ update-browserslist-db@1.2.3:
+ resolution: {integrity: sha512-Js0m9cx+qOgDxo0eMiFGEueWztz+d4+M3rGlmKPT+T4IS/jP4ylw3Nwpu6cpTTP8R1MAC1kF4VbdLt3ARf209w==}
+ hasBin: true
+ peerDependencies:
+ browserslist: '>= 4.21.0'
+
uri-js@4.4.1:
resolution: {integrity: sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==}
@@ -776,29 +1333,266 @@ packages:
yaml:
optional: true
+ vitest@4.0.17:
+ resolution: {integrity: sha512-FQMeF0DJdWY0iOnbv466n/0BudNdKj1l5jYgl5JVTwjSsZSlqyXFt/9+1sEyhR6CLowbZpV7O1sCHrzBhucKKg==}
+ engines: {node: ^20.0.0 || ^22.0.0 || >=24.0.0}
+ hasBin: true
+ peerDependencies:
+ '@edge-runtime/vm': '*'
+ '@opentelemetry/api': ^1.9.0
+ '@types/node': ^20.0.0 || ^22.0.0 || >=24.0.0
+ '@vitest/browser-playwright': 4.0.17
+ '@vitest/browser-preview': 4.0.17
+ '@vitest/browser-webdriverio': 4.0.17
+ '@vitest/ui': 4.0.17
+ happy-dom: '*'
+ jsdom: '*'
+ peerDependenciesMeta:
+ '@edge-runtime/vm':
+ optional: true
+ '@opentelemetry/api':
+ optional: true
+ '@types/node':
+ optional: true
+ '@vitest/browser-playwright':
+ optional: true
+ '@vitest/browser-preview':
+ optional: true
+ '@vitest/browser-webdriverio':
+ optional: true
+ '@vitest/ui':
+ optional: true
+ happy-dom:
+ optional: true
+ jsdom:
+ optional: true
+
vscode-uri@3.1.0:
resolution: {integrity: sha512-/BpdSx+yCQGnCvecbyXdxHDkuk55/G3xwnC0GqY4gmQ3j+A+g8kzzgB4Nk/SINjqn6+waqw3EgbVF2QKExkRxQ==}
- yallist@4.0.0:
- resolution: {integrity: sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==}
+ w3c-xmlserializer@5.0.0:
+ resolution: {integrity: sha512-o8qghlI8NZHU1lLPrpi2+Uq7abh4GGPpYANlalzWxyWteJOCsr/P+oPBA49TOLu5FTZO4d3F9MnWJfiMo4BkmA==}
+ engines: {node: '>=18'}
-snapshots:
+ webidl-conversions@8.0.1:
+ resolution: {integrity: sha512-BMhLD/Sw+GbJC21C/UgyaZX41nPt8bUTg+jWyDeg7e7YN4xOM05YPSIXceACnXVtqyEw/LMClUQMtMZ+PGGpqQ==}
+ engines: {node: '>=20'}
- '@babel/helper-string-parser@7.27.1': {}
+ whatwg-mimetype@3.0.0:
+ resolution: {integrity: sha512-nt+N2dzIutVRxARx1nghPKGv1xHikU7HKdfafKkLNLindmPU/ch3U31NOCGGA/dmPcmb1VlofO0vnKAcsm0o/Q==}
+ engines: {node: '>=12'}
- '@babel/helper-validator-identifier@7.28.5': {}
+ whatwg-mimetype@4.0.0:
+ resolution: {integrity: sha512-QaKxh0eNIi2mE9p2vEdzfagOKHCcj1pJ56EEHGQOVxp8r9/iszLUUV7v89x9O1p/T+NlTM5W7jW6+cz4Fq1YVg==}
+ engines: {node: '>=18'}
- '@babel/parser@7.28.5':
- dependencies:
- '@babel/types': 7.28.5
+ whatwg-url@15.1.0:
+ resolution: {integrity: sha512-2ytDk0kiEj/yu90JOAp44PVPUkO9+jVhyf+SybKlRHSDlvOOZhdPIrr7xTH64l4WixO2cP+wQIcgujkGBPPz6g==}
+ engines: {node: '>=20'}
- '@babel/types@7.28.5':
- dependencies:
- '@babel/helper-string-parser': 7.27.1
- '@babel/helper-validator-identifier': 7.28.5
+ why-is-node-running@2.3.0:
+ resolution: {integrity: sha512-hUrmaWBdVDcxvYqnyh09zunKzROWjbZTiNy8dBEjkS7ehEDQibXJ7XvlmtbwuTclUiIyN+CyXQD4Vmko8fNm8w==}
+ engines: {node: '>=8'}
+ hasBin: true
- '@esbuild/aix-ppc64@0.25.12':
- optional: true
+ ws@8.19.0:
+ resolution: {integrity: sha512-blAT2mjOEIi0ZzruJfIhb3nps74PRWTCz1IjglWEEpQl5XS/UNama6u2/rjFkDDouqr4L67ry+1aGIALViWjDg==}
+ engines: {node: '>=10.0.0'}
+ peerDependencies:
+ bufferutil: ^4.0.1
+ utf-8-validate: '>=5.0.2'
+ peerDependenciesMeta:
+ bufferutil:
+ optional: true
+ utf-8-validate:
+ optional: true
+
+ xml-name-validator@5.0.0:
+ resolution: {integrity: sha512-EvGK8EJ3DhaHfbRlETOWAS5pO9MZITeauHKJyb8wyajUfQUenkIg2MvLDTZ4T/TgIcm3HU0TFBgWWboAZ30UHg==}
+ engines: {node: '>=18'}
+
+ xmlchars@2.2.0:
+ resolution: {integrity: sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==}
+
+ yallist@3.1.1:
+ resolution: {integrity: sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==}
+
+ yallist@4.0.0:
+ resolution: {integrity: sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==}
+
+snapshots:
+
+ '@acemir/cssom@0.9.31': {}
+
+ '@adobe/css-tools@4.4.4': {}
+
+ '@asamuzakjp/css-color@4.1.1':
+ dependencies:
+ '@csstools/css-calc': 2.1.4(@csstools/css-parser-algorithms@3.0.5(@csstools/css-tokenizer@3.0.4))(@csstools/css-tokenizer@3.0.4)
+ '@csstools/css-color-parser': 3.1.0(@csstools/css-parser-algorithms@3.0.5(@csstools/css-tokenizer@3.0.4))(@csstools/css-tokenizer@3.0.4)
+ '@csstools/css-parser-algorithms': 3.0.5(@csstools/css-tokenizer@3.0.4)
+ '@csstools/css-tokenizer': 3.0.4
+ lru-cache: 11.2.4
+
+ '@asamuzakjp/dom-selector@6.7.6':
+ dependencies:
+ '@asamuzakjp/nwsapi': 2.3.9
+ bidi-js: 1.0.3
+ css-tree: 3.1.0
+ is-potential-custom-element-name: 1.0.1
+ lru-cache: 11.2.4
+
+ '@asamuzakjp/nwsapi@2.3.9': {}
+
+ '@babel/code-frame@7.28.6':
+ dependencies:
+ '@babel/helper-validator-identifier': 7.28.5
+ js-tokens: 4.0.0
+ picocolors: 1.1.1
+
+ '@babel/compat-data@7.28.6': {}
+
+ '@babel/core@7.28.6':
+ dependencies:
+ '@babel/code-frame': 7.28.6
+ '@babel/generator': 7.28.6
+ '@babel/helper-compilation-targets': 7.28.6
+ '@babel/helper-module-transforms': 7.28.6(@babel/core@7.28.6)
+ '@babel/helpers': 7.28.6
+ '@babel/parser': 7.28.6
+ '@babel/template': 7.28.6
+ '@babel/traverse': 7.28.6
+ '@babel/types': 7.28.6
+ '@jridgewell/remapping': 2.3.5
+ convert-source-map: 2.0.0
+ debug: 4.4.3
+ gensync: 1.0.0-beta.2
+ json5: 2.2.3
+ semver: 6.3.1
+ transitivePeerDependencies:
+ - supports-color
+
+ '@babel/generator@7.28.6':
+ dependencies:
+ '@babel/parser': 7.28.6
+ '@babel/types': 7.28.6
+ '@jridgewell/gen-mapping': 0.3.13
+ '@jridgewell/trace-mapping': 0.3.31
+ jsesc: 3.1.0
+
+ '@babel/helper-compilation-targets@7.28.6':
+ dependencies:
+ '@babel/compat-data': 7.28.6
+ '@babel/helper-validator-option': 7.27.1
+ browserslist: 4.28.1
+ lru-cache: 5.1.1
+ semver: 6.3.1
+
+ '@babel/helper-globals@7.28.0': {}
+
+ '@babel/helper-module-imports@7.28.6':
+ dependencies:
+ '@babel/traverse': 7.28.6
+ '@babel/types': 7.28.6
+ transitivePeerDependencies:
+ - supports-color
+
+ '@babel/helper-module-transforms@7.28.6(@babel/core@7.28.6)':
+ dependencies:
+ '@babel/core': 7.28.6
+ '@babel/helper-module-imports': 7.28.6
+ '@babel/helper-validator-identifier': 7.28.5
+ '@babel/traverse': 7.28.6
+ transitivePeerDependencies:
+ - supports-color
+
+ '@babel/helper-plugin-utils@7.28.6': {}
+
+ '@babel/helper-string-parser@7.27.1': {}
+
+ '@babel/helper-validator-identifier@7.28.5': {}
+
+ '@babel/helper-validator-option@7.27.1': {}
+
+ '@babel/helpers@7.28.6':
+ dependencies:
+ '@babel/template': 7.28.6
+ '@babel/types': 7.28.6
+
+ '@babel/parser@7.28.5':
+ dependencies:
+ '@babel/types': 7.28.5
+
+ '@babel/parser@7.28.6':
+ dependencies:
+ '@babel/types': 7.28.6
+
+ '@babel/plugin-transform-react-jsx-self@7.27.1(@babel/core@7.28.6)':
+ dependencies:
+ '@babel/core': 7.28.6
+ '@babel/helper-plugin-utils': 7.28.6
+
+ '@babel/plugin-transform-react-jsx-source@7.27.1(@babel/core@7.28.6)':
+ dependencies:
+ '@babel/core': 7.28.6
+ '@babel/helper-plugin-utils': 7.28.6
+
+ '@babel/runtime@7.28.6': {}
+
+ '@babel/template@7.28.6':
+ dependencies:
+ '@babel/code-frame': 7.28.6
+ '@babel/parser': 7.28.6
+ '@babel/types': 7.28.6
+
+ '@babel/traverse@7.28.6':
+ dependencies:
+ '@babel/code-frame': 7.28.6
+ '@babel/generator': 7.28.6
+ '@babel/helper-globals': 7.28.0
+ '@babel/parser': 7.28.6
+ '@babel/template': 7.28.6
+ '@babel/types': 7.28.6
+ debug: 4.4.3
+ transitivePeerDependencies:
+ - supports-color
+
+ '@babel/types@7.28.5':
+ dependencies:
+ '@babel/helper-string-parser': 7.27.1
+ '@babel/helper-validator-identifier': 7.28.5
+
+ '@babel/types@7.28.6':
+ dependencies:
+ '@babel/helper-string-parser': 7.27.1
+ '@babel/helper-validator-identifier': 7.28.5
+
+ '@bcoe/v8-coverage@1.0.2': {}
+
+ '@csstools/color-helpers@5.1.0': {}
+
+ '@csstools/css-calc@2.1.4(@csstools/css-parser-algorithms@3.0.5(@csstools/css-tokenizer@3.0.4))(@csstools/css-tokenizer@3.0.4)':
+ dependencies:
+ '@csstools/css-parser-algorithms': 3.0.5(@csstools/css-tokenizer@3.0.4)
+ '@csstools/css-tokenizer': 3.0.4
+
+ '@csstools/css-color-parser@3.1.0(@csstools/css-parser-algorithms@3.0.5(@csstools/css-tokenizer@3.0.4))(@csstools/css-tokenizer@3.0.4)':
+ dependencies:
+ '@csstools/color-helpers': 5.1.0
+ '@csstools/css-calc': 2.1.4(@csstools/css-parser-algorithms@3.0.5(@csstools/css-tokenizer@3.0.4))(@csstools/css-tokenizer@3.0.4)
+ '@csstools/css-parser-algorithms': 3.0.5(@csstools/css-tokenizer@3.0.4)
+ '@csstools/css-tokenizer': 3.0.4
+
+ '@csstools/css-parser-algorithms@3.0.5(@csstools/css-tokenizer@3.0.4)':
+ dependencies:
+ '@csstools/css-tokenizer': 3.0.4
+
+ '@csstools/css-syntax-patches-for-csstree@1.0.25': {}
+
+ '@csstools/css-tokenizer@3.0.4': {}
+
+ '@esbuild/aix-ppc64@0.25.12':
+ optional: true
'@esbuild/android-arm64@0.25.12':
optional: true
@@ -875,14 +1669,33 @@ snapshots:
'@esbuild/win32-x64@0.25.12':
optional: true
+ '@exodus/bytes@1.8.0': {}
+
'@isaacs/balanced-match@4.0.1': {}
'@isaacs/brace-expansion@5.0.0':
dependencies:
'@isaacs/balanced-match': 4.0.1
+ '@jridgewell/gen-mapping@0.3.13':
+ dependencies:
+ '@jridgewell/sourcemap-codec': 1.5.5
+ '@jridgewell/trace-mapping': 0.3.31
+
+ '@jridgewell/remapping@2.3.5':
+ dependencies:
+ '@jridgewell/gen-mapping': 0.3.13
+ '@jridgewell/trace-mapping': 0.3.31
+
+ '@jridgewell/resolve-uri@3.1.2': {}
+
'@jridgewell/sourcemap-codec@1.5.5': {}
+ '@jridgewell/trace-mapping@0.3.31':
+ dependencies:
+ '@jridgewell/resolve-uri': 3.1.2
+ '@jridgewell/sourcemap-codec': 1.5.5
+
'@microsoft/api-extractor-model@7.32.2(@types/node@22.19.3)':
dependencies:
'@microsoft/tsdoc': 0.16.0
@@ -919,6 +1732,8 @@ snapshots:
'@microsoft/tsdoc@0.16.0': {}
+ '@rolldown/pluginutils@1.0.0-beta.53': {}
+
'@rollup/pluginutils@5.3.0(rollup@4.55.1)':
dependencies:
'@types/estree': 1.0.8
@@ -1041,10 +1856,75 @@ snapshots:
transitivePeerDependencies:
- '@types/node'
+ '@standard-schema/spec@1.1.0': {}
+
+ '@testing-library/dom@10.4.1':
+ dependencies:
+ '@babel/code-frame': 7.28.6
+ '@babel/runtime': 7.28.6
+ '@types/aria-query': 5.0.4
+ aria-query: 5.3.0
+ dom-accessibility-api: 0.5.16
+ lz-string: 1.5.0
+ picocolors: 1.1.1
+ pretty-format: 27.5.1
+
+ '@testing-library/jest-dom@6.9.1':
+ dependencies:
+ '@adobe/css-tools': 4.4.4
+ aria-query: 5.3.2
+ css.escape: 1.5.1
+ dom-accessibility-api: 0.6.3
+ picocolors: 1.1.1
+ redent: 3.0.0
+
+ '@testing-library/react@16.3.1(@testing-library/dom@10.4.1)(@types/react@19.2.7)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)':
+ dependencies:
+ '@babel/runtime': 7.28.6
+ '@testing-library/dom': 10.4.1
+ react: 19.2.3
+ react-dom: 19.2.3(react@19.2.3)
+ optionalDependencies:
+ '@types/react': 19.2.7
+
'@types/argparse@1.0.38': {}
+ '@types/aria-query@5.0.4': {}
+
+ '@types/babel__core@7.20.5':
+ dependencies:
+ '@babel/parser': 7.28.5
+ '@babel/types': 7.28.5
+ '@types/babel__generator': 7.27.0
+ '@types/babel__template': 7.4.4
+ '@types/babel__traverse': 7.28.0
+
+ '@types/babel__generator@7.27.0':
+ dependencies:
+ '@babel/types': 7.28.5
+
+ '@types/babel__template@7.4.4':
+ dependencies:
+ '@babel/parser': 7.28.5
+ '@babel/types': 7.28.5
+
+ '@types/babel__traverse@7.28.0':
+ dependencies:
+ '@babel/types': 7.28.5
+
+ '@types/chai@5.2.3':
+ dependencies:
+ '@types/deep-eql': 4.0.2
+ assertion-error: 2.0.1
+
+ '@types/deep-eql@4.0.2': {}
+
'@types/estree@1.0.8': {}
+ '@types/node@20.19.29':
+ dependencies:
+ undici-types: 6.21.0
+
'@types/node@22.19.3':
dependencies:
undici-types: 6.21.0
@@ -1053,6 +1933,77 @@ snapshots:
dependencies:
csstype: 3.2.3
+ '@types/whatwg-mimetype@3.0.2': {}
+
+ '@types/ws@8.18.1':
+ dependencies:
+ '@types/node': 22.19.3
+
+ '@vitejs/plugin-react@5.1.2(vite@6.4.1(@types/node@22.19.3))':
+ dependencies:
+ '@babel/core': 7.28.6
+ '@babel/plugin-transform-react-jsx-self': 7.27.1(@babel/core@7.28.6)
+ '@babel/plugin-transform-react-jsx-source': 7.27.1(@babel/core@7.28.6)
+ '@rolldown/pluginutils': 1.0.0-beta.53
+ '@types/babel__core': 7.20.5
+ react-refresh: 0.18.0
+ vite: 6.4.1(@types/node@22.19.3)
+ transitivePeerDependencies:
+ - supports-color
+
+ '@vitest/coverage-v8@4.0.17(vitest@4.0.17(@types/node@22.19.3)(happy-dom@20.1.0)(jsdom@27.4.0))':
+ dependencies:
+ '@bcoe/v8-coverage': 1.0.2
+ '@vitest/utils': 4.0.17
+ ast-v8-to-istanbul: 0.3.10
+ istanbul-lib-coverage: 3.2.2
+ istanbul-lib-report: 3.0.1
+ istanbul-reports: 3.2.0
+ magicast: 0.5.1
+ obug: 2.1.1
+ std-env: 3.10.0
+ tinyrainbow: 3.0.3
+ vitest: 4.0.17(@types/node@22.19.3)(happy-dom@20.1.0)(jsdom@27.4.0)
+
+ '@vitest/expect@4.0.17':
+ dependencies:
+ '@standard-schema/spec': 1.1.0
+ '@types/chai': 5.2.3
+ '@vitest/spy': 4.0.17
+ '@vitest/utils': 4.0.17
+ chai: 6.2.2
+ tinyrainbow: 3.0.3
+
+ '@vitest/mocker@4.0.17(vite@6.4.1(@types/node@22.19.3))':
+ dependencies:
+ '@vitest/spy': 4.0.17
+ estree-walker: 3.0.3
+ magic-string: 0.30.21
+ optionalDependencies:
+ vite: 6.4.1(@types/node@22.19.3)
+
+ '@vitest/pretty-format@4.0.17':
+ dependencies:
+ tinyrainbow: 3.0.3
+
+ '@vitest/runner@4.0.17':
+ dependencies:
+ '@vitest/utils': 4.0.17
+ pathe: 2.0.3
+
+ '@vitest/snapshot@4.0.17':
+ dependencies:
+ '@vitest/pretty-format': 4.0.17
+ magic-string: 0.30.21
+ pathe: 2.0.3
+
+ '@vitest/spy@4.0.17': {}
+
+ '@vitest/utils@4.0.17':
+ dependencies:
+ '@vitest/pretty-format': 4.0.17
+ tinyrainbow: 3.0.3
+
'@volar/language-core@2.4.27':
dependencies:
'@volar/source-map': 2.4.27
@@ -1100,6 +2051,8 @@ snapshots:
acorn@8.15.0: {}
+ agent-base@7.1.4: {}
+
ajv-draft-04@1.0.0(ajv@8.13.0):
optionalDependencies:
ajv: 8.13.0
@@ -1124,34 +2077,105 @@ snapshots:
alien-signals@0.4.14: {}
+ ansi-regex@5.0.1: {}
+
+ ansi-styles@5.2.0: {}
+
argparse@1.0.10:
dependencies:
sprintf-js: 1.0.3
+ aria-query@5.3.0:
+ dependencies:
+ dequal: 2.0.3
+
+ aria-query@5.3.2: {}
+
+ assertion-error@2.0.1: {}
+
+ ast-v8-to-istanbul@0.3.10:
+ dependencies:
+ '@jridgewell/trace-mapping': 0.3.31
+ estree-walker: 3.0.3
+ js-tokens: 9.0.1
+
balanced-match@1.0.2: {}
+ baseline-browser-mapping@2.9.14: {}
+
+ bidi-js@1.0.3:
+ dependencies:
+ require-from-string: 2.0.2
+
brace-expansion@2.0.2:
dependencies:
balanced-match: 1.0.2
+ browserslist@4.28.1:
+ dependencies:
+ baseline-browser-mapping: 2.9.14
+ caniuse-lite: 1.0.30001764
+ electron-to-chromium: 1.5.267
+ node-releases: 2.0.27
+ update-browserslist-db: 1.2.3(browserslist@4.28.1)
+
+ caniuse-lite@1.0.30001764: {}
+
+ chai@6.2.2: {}
+
compare-versions@6.1.1: {}
confbox@0.1.8: {}
confbox@0.2.2: {}
+ convert-source-map@2.0.0: {}
+
+ css-tree@3.1.0:
+ dependencies:
+ mdn-data: 2.12.2
+ source-map-js: 1.2.1
+
+ css.escape@1.5.1: {}
+
+ cssstyle@5.3.7:
+ dependencies:
+ '@asamuzakjp/css-color': 4.1.1
+ '@csstools/css-syntax-patches-for-csstree': 1.0.25
+ css-tree: 3.1.0
+ lru-cache: 11.2.4
+
csstype@3.2.3: {}
+ data-urls@6.0.0:
+ dependencies:
+ whatwg-mimetype: 4.0.0
+ whatwg-url: 15.1.0
+
de-indent@1.0.2: {}
debug@4.4.3:
dependencies:
ms: 2.1.3
+ decimal.js@10.6.0: {}
+
+ dequal@2.0.3: {}
+
diff@8.0.2: {}
+ dom-accessibility-api@0.5.16: {}
+
+ dom-accessibility-api@0.6.3: {}
+
+ electron-to-chromium@1.5.267: {}
+
+ entities@6.0.1: {}
+
entities@7.0.0: {}
+ es-module-lexer@1.7.0: {}
+
esbuild@0.25.12:
optionalDependencies:
'@esbuild/aix-ppc64': 0.25.12
@@ -1181,8 +2205,16 @@ snapshots:
'@esbuild/win32-ia32': 0.25.12
'@esbuild/win32-x64': 0.25.12
+ escalade@3.2.0: {}
+
estree-walker@2.0.2: {}
+ estree-walker@3.0.3:
+ dependencies:
+ '@types/estree': 1.0.8
+
+ expect-type@1.3.0: {}
+
exsolve@1.0.8: {}
fast-deep-equal@3.1.3: {}
@@ -1202,8 +2234,21 @@ snapshots:
function-bind@1.1.2: {}
+ gensync@1.0.0-beta.2: {}
+
graceful-fs@4.2.11: {}
+ happy-dom@20.1.0:
+ dependencies:
+ '@types/node': 20.19.29
+ '@types/whatwg-mimetype': 3.0.2
+ '@types/ws': 8.18.1
+ whatwg-mimetype: 3.0.0
+ ws: 8.19.0
+ transitivePeerDependencies:
+ - bufferutil
+ - utf-8-validate
+
has-flag@4.0.0: {}
hasown@2.0.2:
@@ -1212,16 +2257,91 @@ snapshots:
he@1.2.0: {}
+ html-encoding-sniffer@6.0.0:
+ dependencies:
+ '@exodus/bytes': 1.8.0
+ transitivePeerDependencies:
+ - '@exodus/crypto'
+
+ html-escaper@2.0.2: {}
+
+ http-proxy-agent@7.0.2:
+ dependencies:
+ agent-base: 7.1.4
+ debug: 4.4.3
+ transitivePeerDependencies:
+ - supports-color
+
+ https-proxy-agent@7.0.6:
+ dependencies:
+ agent-base: 7.1.4
+ debug: 4.4.3
+ transitivePeerDependencies:
+ - supports-color
+
import-lazy@4.0.0: {}
+ indent-string@4.0.0: {}
+
is-core-module@2.16.1:
dependencies:
hasown: 2.0.2
+ is-potential-custom-element-name@1.0.1: {}
+
+ istanbul-lib-coverage@3.2.2: {}
+
+ istanbul-lib-report@3.0.1:
+ dependencies:
+ istanbul-lib-coverage: 3.2.2
+ make-dir: 4.0.0
+ supports-color: 7.2.0
+
+ istanbul-reports@3.2.0:
+ dependencies:
+ html-escaper: 2.0.2
+ istanbul-lib-report: 3.0.1
+
jju@1.4.0: {}
+ js-tokens@4.0.0: {}
+
+ js-tokens@9.0.1: {}
+
+ jsdom@27.4.0:
+ dependencies:
+ '@acemir/cssom': 0.9.31
+ '@asamuzakjp/dom-selector': 6.7.6
+ '@exodus/bytes': 1.8.0
+ cssstyle: 5.3.7
+ data-urls: 6.0.0
+ decimal.js: 10.6.0
+ html-encoding-sniffer: 6.0.0
+ http-proxy-agent: 7.0.2
+ https-proxy-agent: 7.0.6
+ is-potential-custom-element-name: 1.0.1
+ parse5: 8.0.0
+ saxes: 6.0.0
+ symbol-tree: 3.2.4
+ tough-cookie: 6.0.0
+ w3c-xmlserializer: 5.0.0
+ webidl-conversions: 8.0.1
+ whatwg-mimetype: 4.0.0
+ whatwg-url: 15.1.0
+ ws: 8.19.0
+ xml-name-validator: 5.0.0
+ transitivePeerDependencies:
+ - '@exodus/crypto'
+ - bufferutil
+ - supports-color
+ - utf-8-validate
+
+ jsesc@3.1.0: {}
+
json-schema-traverse@1.0.0: {}
+ json5@2.2.3: {}
+
jsonfile@6.2.0:
dependencies:
universalify: 2.0.1
@@ -1238,14 +2358,36 @@ snapshots:
lodash@4.17.21: {}
+ lru-cache@11.2.4: {}
+
+ lru-cache@5.1.1:
+ dependencies:
+ yallist: 3.1.1
+
lru-cache@6.0.0:
dependencies:
yallist: 4.0.0
+ lz-string@1.5.0: {}
+
magic-string@0.30.21:
dependencies:
'@jridgewell/sourcemap-codec': 1.5.5
+ magicast@0.5.1:
+ dependencies:
+ '@babel/parser': 7.28.6
+ '@babel/types': 7.28.6
+ source-map-js: 1.2.1
+
+ make-dir@4.0.0:
+ dependencies:
+ semver: 7.5.4
+
+ mdn-data@2.12.2: {}
+
+ min-indent@1.0.1: {}
+
minimatch@10.0.3:
dependencies:
'@isaacs/brace-expansion': 5.0.0
@@ -1267,6 +2409,14 @@ snapshots:
nanoid@3.3.11: {}
+ node-releases@2.0.27: {}
+
+ obug@2.1.1: {}
+
+ parse5@8.0.0:
+ dependencies:
+ entities: 6.0.1
+
path-browserify@1.0.1: {}
path-parse@1.0.7: {}
@@ -1297,12 +2447,32 @@ snapshots:
prettier@3.7.4: {}
+ pretty-format@27.5.1:
+ dependencies:
+ ansi-regex: 5.0.1
+ ansi-styles: 5.2.0
+ react-is: 17.0.2
+
punycode@2.3.1: {}
quansync@0.2.11: {}
+ react-dom@19.2.3(react@19.2.3):
+ dependencies:
+ react: 19.2.3
+ scheduler: 0.27.0
+
+ react-is@17.0.2: {}
+
+ react-refresh@0.18.0: {}
+
react@19.2.3: {}
+ redent@3.0.0:
+ dependencies:
+ indent-string: 4.0.0
+ strip-indent: 3.0.0
+
require-from-string@2.0.2: {}
resolve@1.22.11:
@@ -1342,31 +2512,75 @@ snapshots:
'@rollup/rollup-win32-x64-msvc': 4.55.1
fsevents: 2.3.3
+ saxes@6.0.0:
+ dependencies:
+ xmlchars: 2.2.0
+
+ scheduler@0.27.0: {}
+
+ semver@6.3.1: {}
+
semver@7.5.4:
dependencies:
lru-cache: 6.0.0
+ siginfo@2.0.0: {}
+
source-map-js@1.2.1: {}
source-map@0.6.1: {}
sprintf-js@1.0.3: {}
+ stackback@0.0.2: {}
+
+ std-env@3.10.0: {}
+
string-argv@0.3.2: {}
+ strip-indent@3.0.0:
+ dependencies:
+ min-indent: 1.0.1
+
strip-json-comments@3.1.1: {}
+ supports-color@7.2.0:
+ dependencies:
+ has-flag: 4.0.0
+
supports-color@8.1.1:
dependencies:
has-flag: 4.0.0
supports-preserve-symlinks-flag@1.0.0: {}
+ symbol-tree@3.2.4: {}
+
+ tinybench@2.9.0: {}
+
+ tinyexec@1.0.2: {}
+
tinyglobby@0.2.15:
dependencies:
fdir: 6.5.0(picomatch@4.0.3)
picomatch: 4.0.3
+ tinyrainbow@3.0.3: {}
+
+ tldts-core@7.0.19: {}
+
+ tldts@7.0.19:
+ dependencies:
+ tldts-core: 7.0.19
+
+ tough-cookie@6.0.0:
+ dependencies:
+ tldts: 7.0.19
+
+ tr46@6.0.0:
+ dependencies:
+ punycode: 2.3.1
+
typescript@5.8.2: {}
typescript@5.9.3: {}
@@ -1377,6 +2591,12 @@ snapshots:
universalify@2.0.1: {}
+ update-browserslist-db@1.2.3(browserslist@4.28.1):
+ dependencies:
+ browserslist: 4.28.1
+ escalade: 3.2.0
+ picocolors: 1.1.1
+
uri-js@4.4.1:
dependencies:
punycode: 2.3.1
@@ -1412,6 +2632,73 @@ snapshots:
'@types/node': 22.19.3
fsevents: 2.3.3
+ vitest@4.0.17(@types/node@22.19.3)(happy-dom@20.1.0)(jsdom@27.4.0):
+ dependencies:
+ '@vitest/expect': 4.0.17
+ '@vitest/mocker': 4.0.17(vite@6.4.1(@types/node@22.19.3))
+ '@vitest/pretty-format': 4.0.17
+ '@vitest/runner': 4.0.17
+ '@vitest/snapshot': 4.0.17
+ '@vitest/spy': 4.0.17
+ '@vitest/utils': 4.0.17
+ es-module-lexer: 1.7.0
+ expect-type: 1.3.0
+ magic-string: 0.30.21
+ obug: 2.1.1
+ pathe: 2.0.3
+ picomatch: 4.0.3
+ std-env: 3.10.0
+ tinybench: 2.9.0
+ tinyexec: 1.0.2
+ tinyglobby: 0.2.15
+ tinyrainbow: 3.0.3
+ vite: 6.4.1(@types/node@22.19.3)
+ why-is-node-running: 2.3.0
+ optionalDependencies:
+ '@types/node': 22.19.3
+ happy-dom: 20.1.0
+ jsdom: 27.4.0
+ transitivePeerDependencies:
+ - jiti
+ - less
+ - lightningcss
+ - msw
+ - sass
+ - sass-embedded
+ - stylus
+ - sugarss
+ - terser
+ - tsx
+ - yaml
+
vscode-uri@3.1.0: {}
+ w3c-xmlserializer@5.0.0:
+ dependencies:
+ xml-name-validator: 5.0.0
+
+ webidl-conversions@8.0.1: {}
+
+ whatwg-mimetype@3.0.0: {}
+
+ whatwg-mimetype@4.0.0: {}
+
+ whatwg-url@15.1.0:
+ dependencies:
+ tr46: 6.0.0
+ webidl-conversions: 8.0.1
+
+ why-is-node-running@2.3.0:
+ dependencies:
+ siginfo: 2.0.0
+ stackback: 0.0.2
+
+ ws@8.19.0: {}
+
+ xml-name-validator@5.0.0: {}
+
+ xmlchars@2.2.0: {}
+
+ yallist@3.1.1: {}
+
yallist@4.0.0: {}
diff --git a/src/test/ReactHeadSafe.test.tsx b/src/test/ReactHeadSafe.test.tsx
new file mode 100644
index 0000000..3459a81
--- /dev/null
+++ b/src/test/ReactHeadSafe.test.tsx
@@ -0,0 +1,247 @@
+import React from 'react';
+import { describe, it, expect, beforeEach } from 'vitest';
+import { render } from '@testing-library/react';
+import { ReactHeadSafe } from '../ReactHeadSafe';
+
+describe('ReactHeadSafe', () => {
+ beforeEach(() => {
+ // Reset document head before each test
+ document.head.innerHTML = '';
+ document.title = '';
+ });
+
+ describe('title', () => {
+ it('should set document title', () => {
+ render();
+ expect(document.title).toBe('Test Title');
+ });
+
+ it('should update document title when prop changes', () => {
+ const { rerender } = render();
+ expect(document.title).toBe('Initial Title');
+
+ rerender();
+ expect(document.title).toBe('Updated Title');
+ });
+
+ it('should not change document title when title prop is undefined', () => {
+ document.title = 'Existing Title';
+ render();
+ expect(document.title).toBe('Existing Title');
+ });
+ });
+
+ describe('description meta tag', () => {
+ it('should create description meta tag', () => {
+ render();
+
+ const metaTag = document.querySelector('meta[name="description"]');
+ expect(metaTag).toBeInTheDocument();
+ expect(metaTag?.getAttribute('content')).toBe('Test Description');
+ });
+
+ it('should update description meta tag when prop changes', () => {
+ const { rerender } = render(
+
+ );
+
+ let metaTag = document.querySelector('meta[name="description"]');
+ expect(metaTag?.getAttribute('content')).toBe('Initial Description');
+
+ rerender();
+
+ metaTag = document.querySelector('meta[name="description"]');
+ expect(metaTag?.getAttribute('content')).toBe('Updated Description');
+ });
+
+ it('should prevent duplicate description meta tags', () => {
+ const { rerender } = render(
+
+ );
+ rerender();
+
+ const metaTags = document.querySelectorAll('meta[name="description"]');
+ expect(metaTags).toHaveLength(1);
+ expect(metaTags[0].getAttribute('content')).toBe('Second Description');
+ });
+ });
+
+ describe('keywords meta tag', () => {
+ it('should create keywords meta tag', () => {
+ render();
+
+ const metaTag = document.querySelector('meta[name="keywords"]');
+ expect(metaTag).toBeInTheDocument();
+ expect(metaTag?.getAttribute('content')).toBe('react, testing, vitest');
+ });
+
+ it('should update keywords meta tag when prop changes', () => {
+ const { rerender } = render(
+
+ );
+
+ let metaTag = document.querySelector('meta[name="keywords"]');
+ expect(metaTag?.getAttribute('content')).toBe('initial, keywords');
+
+ rerender();
+
+ metaTag = document.querySelector('meta[name="keywords"]');
+ expect(metaTag?.getAttribute('content')).toBe('updated, keywords');
+ });
+
+ it('should prevent duplicate keywords meta tags', () => {
+ const { rerender } = render();
+ rerender();
+
+ const metaTags = document.querySelectorAll('meta[name="keywords"]');
+ expect(metaTags).toHaveLength(1);
+ expect(metaTags[0].getAttribute('content')).toBe('second');
+ });
+ });
+
+ describe('Open Graph tags', () => {
+ it('should create og:title meta tag', () => {
+ render();
+
+ const metaTag = document.querySelector('meta[property="og:title"]');
+ expect(metaTag).toBeInTheDocument();
+ expect(metaTag?.getAttribute('content')).toBe('OG Test Title');
+ });
+
+ it('should create og:description meta tag', () => {
+ render();
+
+ const metaTag = document.querySelector('meta[property="og:description"]');
+ expect(metaTag).toBeInTheDocument();
+ expect(metaTag?.getAttribute('content')).toBe('OG Test Description');
+ });
+
+ it('should create og:image meta tag', () => {
+ render();
+
+ const metaTag = document.querySelector('meta[property="og:image"]');
+ expect(metaTag).toBeInTheDocument();
+ expect(metaTag?.getAttribute('content')).toBe(
+ 'https://example.com/image.jpg'
+ );
+ });
+
+ it('should prevent duplicate og:title meta tags', () => {
+ const { rerender } = render();
+ rerender();
+
+ const metaTags = document.querySelectorAll('meta[property="og:title"]');
+ expect(metaTags).toHaveLength(1);
+ expect(metaTags[0].getAttribute('content')).toBe('Second OG Title');
+ });
+
+ it('should prevent duplicate og:description meta tags', () => {
+ const { rerender } = render(
+
+ );
+ rerender();
+
+ const metaTags = document.querySelectorAll(
+ 'meta[property="og:description"]'
+ );
+ expect(metaTags).toHaveLength(1);
+ expect(metaTags[0].getAttribute('content')).toBe('Second OG Description');
+ });
+
+ it('should prevent duplicate og:image meta tags', () => {
+ const { rerender } = render(
+
+ );
+ rerender();
+
+ const metaTags = document.querySelectorAll('meta[property="og:image"]');
+ expect(metaTags).toHaveLength(1);
+ expect(metaTags[0].getAttribute('content')).toBe(
+ 'https://example.com/second.jpg'
+ );
+ });
+ });
+
+ describe('multiple props', () => {
+ it('should handle all props together', () => {
+ render(
+
+ );
+
+ expect(document.title).toBe('Complete Test');
+ expect(
+ document
+ .querySelector('meta[name="description"]')
+ ?.getAttribute('content')
+ ).toBe('Test Description');
+ expect(
+ document.querySelector('meta[name="keywords"]')?.getAttribute('content')
+ ).toBe('test, keywords');
+ expect(
+ document
+ .querySelector('meta[property="og:title"]')
+ ?.getAttribute('content')
+ ).toBe('OG Title');
+ expect(
+ document
+ .querySelector('meta[property="og:description"]')
+ ?.getAttribute('content')
+ ).toBe('OG Description');
+ expect(
+ document
+ .querySelector('meta[property="og:image"]')
+ ?.getAttribute('content')
+ ).toBe('https://example.com/image.jpg');
+ });
+
+ it('should update only changed props', () => {
+ const { rerender } = render(
+
+ );
+
+ expect(document.title).toBe('Initial Title');
+ expect(
+ document
+ .querySelector('meta[name="description"]')
+ ?.getAttribute('content')
+ ).toBe('Initial Description');
+
+ rerender(
+
+ );
+
+ expect(document.title).toBe('Updated Title');
+ expect(
+ document
+ .querySelector('meta[name="description"]')
+ ?.getAttribute('content')
+ ).toBe('Initial Description');
+ });
+ });
+
+ describe('component behavior', () => {
+ it('should render null', () => {
+ const { container } = render();
+ expect(container.firstChild).toBeNull();
+ });
+
+ it('should work with React 17+ (no children required)', () => {
+ expect(() => {
+ render();
+ }).not.toThrow();
+ });
+ });
+});
diff --git a/src/test/setup.ts b/src/test/setup.ts
new file mode 100644
index 0000000..21dd22a
--- /dev/null
+++ b/src/test/setup.ts
@@ -0,0 +1,12 @@
+import '@testing-library/jest-dom';
+import { cleanup } from '@testing-library/react';
+import { afterEach } from 'vitest';
+
+// Clean up after each test
+afterEach(() => {
+ cleanup();
+
+ // Clean up document head after each test
+ document.head.innerHTML = '';
+ document.title = '';
+});
diff --git a/vitest.config.ts b/vitest.config.ts
new file mode 100644
index 0000000..2ad282c
--- /dev/null
+++ b/vitest.config.ts
@@ -0,0 +1,29 @@
+import { defineConfig } from 'vitest/config';
+import react from '@vitejs/plugin-react';
+import { resolve } from 'path';
+
+export default defineConfig({
+ plugins: [react()],
+ test: {
+ environment: 'jsdom',
+ globals: true,
+ setupFiles: './src/test/setup.ts',
+ coverage: {
+ provider: 'v8',
+ reporter: ['text', 'json', 'html'],
+ exclude: [
+ 'node_modules/',
+ 'dist/',
+ 'examples/',
+ 'src/test/',
+ '**/*.config.ts',
+ '**/*.d.ts',
+ ],
+ },
+ },
+ resolve: {
+ alias: {
+ '@': resolve(__dirname, './src'),
+ },
+ },
+});