From 339d5a7be67ac7814d26b89ebc7e3e27ff2e4e3e Mon Sep 17 00:00:00 2001 From: jomzxc Date: Wed, 5 Nov 2025 23:07:30 +0800 Subject: [PATCH 1/8] feat(e2e): add Playwright tests for authentication and profile management --- .github/workflows/ci.yml | 96 +++++++++++++++++++++++++++++ .gitignore | 5 ++ package.json | 7 ++- playwright.config.ts | 87 +++++++++++++++++++++++++++ pnpm-lock.yaml | 96 +++++++++++++++++++++++++++-- tests/auth.spec.ts | 53 ++++++++++++++++ tests/fixtures/test-avatar.png | Bin 0 -> 117519 bytes tests/fixtures/test-file.txt | 1 + tests/global.setup.ts | 56 +++++++++++++++++ tests/profile.spec.ts | 49 +++++++++++++++ tests/transfer.spec.ts | 73 ++++++++++++++++++++++ tests/utils/testmail.helper.ts | 107 +++++++++++++++++++++++++++++++++ 12 files changed, 624 insertions(+), 6 deletions(-) create mode 100644 .github/workflows/ci.yml create mode 100644 playwright.config.ts create mode 100644 tests/auth.spec.ts create mode 100644 tests/fixtures/test-avatar.png create mode 100644 tests/fixtures/test-file.txt create mode 100644 tests/global.setup.ts create mode 100644 tests/profile.spec.ts create mode 100644 tests/transfer.spec.ts create mode 100644 tests/utils/testmail.helper.ts diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..b7d8ab9 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,96 @@ +name: WebDrop CI + +on: + push: + branches: + - main + - develop + pull_request: + branches: + - main + - develop + +jobs: + lint: + name: Lint + runs-on: ubuntu-latest + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Setup pnpm + uses: pnpm/action-setup@v4 + with: + version: 9 + + - name: Setup Node.js + uses: actions/setup-node@v4 + with: + node-version: 20 + cache: "pnpm" + + - name: Install dependencies + run: pnpm install + + - name: Run linter + run: pnpm lint + + test: + name: E2E Tests + runs-on: ubuntu-latest + needs: lint + + # Set environment variables for all test steps + env: + NEXT_PUBLIC_SUPABASE_URL: ${{ secrets.NEXT_PUBLIC_SUPABASE_URL }} + NEXT_PUBLIC_SUPABASE_ANON_KEY: ${{ secrets.NEXT_PUBLIC_SUPABASE_ANON_KEY }} + TESTMAIL_API_KEY: ${{ secrets.TESTMAIL_API_KEY }} + TESTMAIL_NAMESPACE: ${{ secrets.TESTMAIL_NAMESPACE }} # e.g., "abcde" + + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Setup pnpm + uses: pnpm/action-setup@v4 + with: + version: 9 + + - name: Setup Node.js + uses: actions/setup-node@v4 + with: + node-version: 20 + cache: "pnpm" + + - name: Install dependencies + run: pnpm install + + - name: Install Playwright Browsers + run: pnpm exec playwright install --with-deps + + - name: Build Next.js App + run: pnpm build + + - name: Run Playwright tests + run: pnpm test:e2e + + - name: Upload Playwright report + uses: actions/upload-artifact@v4 + if: always() + with: + name: playwright-report + path: playwright-report/ + retention-days: 7 + + # deploy-dev: + # name: Deploy to Development + # runs-on: ubuntu-latest + # needs: test + # if: github.ref == 'refs/heads/develop' + # steps: + # - name: Checkout code + # uses: actions/checkout@v4 + # + # - name: Deploy to Vercel (or other provider) + # # Add your deployment steps here + # run: echo "Deploying to dev..." \ No newline at end of file diff --git a/.gitignore b/.gitignore index 37c2b6f..cae8fb7 100644 --- a/.gitignore +++ b/.gitignore @@ -25,3 +25,8 @@ yarn-error.log* # typescript *.tsbuildinfo next-env.d.ts + +# Playwright +/playwright-report/ +/test-results/ +/playwright/.auth/ \ No newline at end of file diff --git a/package.json b/package.json index bb6c606..970483c 100644 --- a/package.json +++ b/package.json @@ -6,7 +6,10 @@ "build": "next build", "dev": "next dev", "lint": "eslint .", - "start": "next start" + "start": "next start", + "test:e2e": "playwright test", + "test:e2e:ui": "playwright test --ui", + "test:e2e:report": "playwright show-report" }, "dependencies": { "@hookform/resolvers": "^3.10.0", @@ -63,7 +66,9 @@ "zod": "3.25.76" }, "devDependencies": { + "@playwright/test": "^1.45.3", "@tailwindcss/postcss": "^4.1.9", + "@testmail.app/graphql-request": "^1.8.4", "@types/node": "^22", "@types/react": "^19", "@types/react-dom": "^19", diff --git a/playwright.config.ts b/playwright.config.ts new file mode 100644 index 0000000..02ac1e8 --- /dev/null +++ b/playwright.config.ts @@ -0,0 +1,87 @@ +import { defineConfig, devices } from '@playwright/test'; +import path from 'path'; + +// Define storage state paths +export const USER_1_STATE = path.join(__dirname, 'playwright/.auth/user1.json'); +export const USER_2_STATE = path.join(__dirname, 'playwright/.auth/user2.json'); + +export default defineConfig({ + testDir: './tests', + fullyParallel: true, + forbidOnly: !!process.env.CI, + retries: process.env.CI ? 2 : 0, + workers: process.env.CI ? 1 : undefined, + reporter: 'html', + + // Use the dedicated 'setup' project below to seed auth states. + + use: { + baseURL: 'http://localhost:3000', + trace: 'on-first-retry', + }, + + // Configure projects for major browsers + projects: [ + // --- Setup Project --- + // This project runs first to authenticate our two test users. + { + name: 'setup', + testMatch: 'global.setup.ts', + }, + + // --- Main Test Project (Chromium) --- + // This project depends on 'setup' and uses the auth state of User 1. + { + name: 'chromium-user1', + use: { + ...devices['Desktop Chrome'], + storageState: USER_1_STATE, + }, + dependencies: ['setup'], + testIgnore: 'transfer.spec.ts', // Transfer test is special + }, + + // --- Transfer Test Project --- + // This project runs the P2P transfer test, which needs both users. + // It's separated because it has a different setup (launches 2 browsers). + { + name: 'chromium-transfer', + use: { + ...devices['Desktop Chrome'], + }, + dependencies: ['setup'], + testMatch: 'transfer.spec.ts', + }, + + /* + { + name: 'firefox', + use: { + ...devices['Desktop Firefox'], + storageState: USER_1_STATE, + }, + dependencies: ['setup'], + testIgnore: 'transfer.spec.ts', + }, + + { + name: 'webkit', + use: { + ...devices['Desktop Safari'], + storageState: USER_1_STATE, + }, + dependencies: ['setup'], + testIgnore: 'transfer.spec.ts', + }, + */ + ], + + // Run your local dev server before starting the tests + webServer: { + command: 'pnpm start', // Assumes you have run `pnpm build` first + url: 'http://localhost:3000', + reuseExistingServer: !process.env.CI, + stdout: 'ignore', + stderr: 'pipe', + }, +}); \ No newline at end of file diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 2b063ed..a051128 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -100,7 +100,7 @@ importers: version: 2.78.0 '@vercel/analytics': specifier: latest - version: 1.5.0(next@16.0.0(react-dom@19.2.0(react@19.2.0))(react@19.2.0))(react@19.2.0) + version: 1.5.0(next@16.0.0(@playwright/test@1.56.1)(react-dom@19.2.0(react@19.2.0))(react@19.2.0))(react@19.2.0) autoprefixer: specifier: ^10.4.20 version: 10.4.20(postcss@8.5.0) @@ -127,7 +127,7 @@ importers: version: 0.454.0(react@19.2.0) next: specifier: 16.0.0 - version: 16.0.0(react-dom@19.2.0(react@19.2.0))(react@19.2.0) + version: 16.0.0(@playwright/test@1.56.1)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) next-themes: specifier: latest version: 0.4.6(react-dom@19.2.0(react@19.2.0))(react@19.2.0) @@ -165,9 +165,15 @@ importers: specifier: 3.25.76 version: 3.25.76 devDependencies: + '@playwright/test': + specifier: ^1.45.3 + version: 1.56.1 '@tailwindcss/postcss': specifier: ^4.1.9 version: 4.1.9 + '@testmail.app/graphql-request': + specifier: ^1.8.4 + version: 1.8.4 '@types/node': specifier: ^22 version: 22.0.0 @@ -424,6 +430,11 @@ packages: cpu: [x64] os: [win32] + '@playwright/test@1.56.1': + resolution: {integrity: sha512-vSMYtL/zOcFpvJCW71Q/OEGQb7KYBPAdKh35WNSkaZA75JlAO8ED8UN6GUNTm3drWomcbcqRPFqQbLae8yBTdg==} + engines: {node: '>=18'} + hasBin: true + '@radix-ui/number@1.1.0': resolution: {integrity: sha512-V3gRzhVNU1ldS5XhAPTom1fOIo4ccrjjJgmE+LI2h/WaFpHmx0MQApT+KZHnx8abG6Avtfcz4WoEciMnpFT3HQ==} @@ -1436,6 +1447,9 @@ packages: '@tailwindcss/postcss@4.1.9': resolution: {integrity: sha512-v3DKzHibZO8ioVDmuVHCW1PR0XSM7nS40EjZFJEA1xPuvTuQPaR5flE1LyikU3hu2u1KNWBtEaSe8qsQjX3tyg==} + '@testmail.app/graphql-request@1.8.4': + resolution: {integrity: sha512-tHTXfoSLNlZJIKLI/6lpNYriIqigg8Ga8JRatY3978YL4hEvt5DswcRPFAd2O3v/ulv5OfAIzWF66Z5XteNfFw==} + '@types/d3-array@3.2.2': resolution: {integrity: sha512-hOLWVbm7uRza0BYXpIIW5pxfrKe0W+D5lrFiAEYR+pb6w3N2SwSMaJbXdUfSEv+dT4MfHBLtn5js0LAWaO6otw==} @@ -1551,6 +1565,9 @@ packages: resolution: {integrity: sha512-9Kr/j4O16ISv8zBBhJoi4bXOYNTkFLOqSL3UDB0njXxCXNezjeyVrJyGOWtgfs/q2km1gwBcfH8q1yEGoMYunA==} engines: {node: '>=18'} + cross-fetch@3.2.0: + resolution: {integrity: sha512-Q+xVJLoGOeIMXZmbUK4HYk+69cQH6LudR0Vu/pRm2YlU/hDV9CiS0gKUMaWY5f2NeUH9C1nV3bsTlCo0FsTV1Q==} + csstype@3.1.3: resolution: {integrity: sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==} @@ -1637,6 +1654,9 @@ packages: resolution: {integrity: sha512-d4lC8xfavMeBjzGr2vECC3fsGXziXZQyJxD868h2M/mBI3PwAuODxAkLkq5HYuvrPYcUtiLzsTo8U3PgX3Ocww==} engines: {node: '>=10.13.0'} + es6-promise@4.2.8: + resolution: {integrity: sha512-HJDGx5daxeIvxdBxvG2cb9g4tEvwIk3i8+nhX0yGrYmZUzbkdg8QbDevheDB8gd0//uPj4c1EQua8Q+MViT0/w==} + escalade@3.2.0: resolution: {integrity: sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==} engines: {node: '>=6'} @@ -1648,9 +1668,17 @@ packages: resolution: {integrity: sha512-6rxyATwPCkaFIL3JLqw8qXqMpIZ942pTX/tbQFkRsDGblS8tNGtlUauA/+mt6RUfqn/4MoEr+WDkYoIQbibWuQ==} engines: {node: '>=6.0.0'} + fetch-retry@3.2.3: + resolution: {integrity: sha512-baMBEv4uZ1X1cUZAvnM+C9XI7tl4CgHgJE0KBHo3JzuXO7atOeWD5HSkDA2oLYpbzLTZNslFckLkIn6T96hlew==} + fraction.js@4.3.7: resolution: {integrity: sha512-ZsDfxO51wGAXREY55a7la9LScWpwv9RxIrYABrlvOFBlH/ShPnrtsXeuUIfXKKOVicNxQ+o8JTbJvjS4M89yew==} + fsevents@2.3.2: + resolution: {integrity: sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==} + engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} + os: [darwin] + get-nonce@1.0.1: resolution: {integrity: sha512-FJhYRoDaiatfEkUK8HKlicmu/3SGFD51q3itKDGoSTysQJBnfOcxU5GxnhE1E6soB76MbT0MBtnKJuXyAx+96Q==} engines: {node: '>=6'} @@ -1794,6 +1822,15 @@ packages: sass: optional: true + node-fetch@2.7.0: + resolution: {integrity: sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==} + engines: {node: 4.x || >=6.0.0} + peerDependencies: + encoding: ^0.1.0 + peerDependenciesMeta: + encoding: + optional: true + node-releases@2.0.27: resolution: {integrity: sha512-nmh3lCkYZ3grZvqcCH+fjmQ7X+H0OeZgP40OierEaAptX4XofMh5kwNbWh7lBduUzCcV/8kZ+NDLCwm2iorIlA==} @@ -1808,6 +1845,16 @@ packages: picocolors@1.1.1: resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==} + playwright-core@1.56.1: + resolution: {integrity: sha512-hutraynyn31F+Bifme+Ps9Vq59hKuUCz7H1kDOcBs+2oGguKkWTU50bBWrtz34OUWmIwpBTWDxaRPXrIXkgvmQ==} + engines: {node: '>=18'} + hasBin: true + + playwright@1.56.1: + resolution: {integrity: sha512-aFi5B0WovBHTEvpM3DzXTUaeN6eN0qWnTkKx4NQaH4Wvcmc153PdaY2UBdSYKaGYw+UyWXSVyxDUg5DoPEttjw==} + engines: {node: '>=18'} + hasBin: true + postcss-value-parser@4.2.0: resolution: {integrity: sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==} @@ -2218,6 +2265,10 @@ snapshots: '@next/swc-win32-x64-msvc@16.0.0': optional: true + '@playwright/test@1.56.1': + dependencies: + playwright: 1.56.1 + '@radix-ui/number@1.1.0': {} '@radix-ui/number@1.1.1': {} @@ -3253,6 +3304,13 @@ snapshots: postcss: 8.5.0 tailwindcss: 4.1.9 + '@testmail.app/graphql-request@1.8.4': + dependencies: + cross-fetch: 3.2.0 + fetch-retry: 3.2.3 + transitivePeerDependencies: + - encoding + '@types/d3-array@3.2.2': {} '@types/d3-color@3.1.3': {} @@ -3295,9 +3353,9 @@ snapshots: dependencies: '@types/node': 22.0.0 - '@vercel/analytics@1.5.0(next@16.0.0(react-dom@19.2.0(react@19.2.0))(react@19.2.0))(react@19.2.0)': + '@vercel/analytics@1.5.0(next@16.0.0(@playwright/test@1.56.1)(react-dom@19.2.0(react@19.2.0))(react@19.2.0))(react@19.2.0)': optionalDependencies: - next: 16.0.0(react-dom@19.2.0(react@19.2.0))(react@19.2.0) + next: 16.0.0(@playwright/test@1.56.1)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) react: 19.2.0 aria-hidden@1.2.6: @@ -3350,6 +3408,12 @@ snapshots: cookie@1.0.2: {} + cross-fetch@3.2.0: + dependencies: + node-fetch: 2.7.0 + transitivePeerDependencies: + - encoding + csstype@3.1.3: {} d3-array@3.2.4: @@ -3424,14 +3488,23 @@ snapshots: graceful-fs: 4.2.11 tapable: 2.3.0 + es6-promise@4.2.8: {} + escalade@3.2.0: {} eventemitter3@4.0.7: {} fast-equals@5.3.2: {} + fetch-retry@3.2.3: + dependencies: + es6-promise: 4.2.8 + fraction.js@4.3.7: {} + fsevents@2.3.2: + optional: true + get-nonce@1.0.1: {} graceful-fs@4.2.11: {} @@ -3519,7 +3592,7 @@ snapshots: react: 19.2.0 react-dom: 19.2.0(react@19.2.0) - next@16.0.0(react-dom@19.2.0(react@19.2.0))(react@19.2.0): + next@16.0.0(@playwright/test@1.56.1)(react-dom@19.2.0(react@19.2.0))(react@19.2.0): dependencies: '@next/env': 16.0.0 '@swc/helpers': 0.5.15 @@ -3537,11 +3610,16 @@ snapshots: '@next/swc-linux-x64-musl': 16.0.0 '@next/swc-win32-arm64-msvc': 16.0.0 '@next/swc-win32-x64-msvc': 16.0.0 + '@playwright/test': 1.56.1 sharp: 0.34.4 transitivePeerDependencies: - '@babel/core' - babel-plugin-macros + node-fetch@2.7.0: + dependencies: + whatwg-url: 5.0.0 + node-releases@2.0.27: {} normalize-range@0.1.2: {} @@ -3550,6 +3628,14 @@ snapshots: picocolors@1.1.1: {} + playwright-core@1.56.1: {} + + playwright@1.56.1: + dependencies: + playwright-core: 1.56.1 + optionalDependencies: + fsevents: 2.3.2 + postcss-value-parser@4.2.0: {} postcss@8.4.31: diff --git a/tests/auth.spec.ts b/tests/auth.spec.ts new file mode 100644 index 0000000..12d4e25 --- /dev/null +++ b/tests/auth.spec.ts @@ -0,0 +1,53 @@ +import { test, expect } from '@playwright/test'; + +test.describe('Authentication', () => { + + test('should sign out successfully', async ({ page }) => { + // Page starts authenticated due to `storageState` + await page.goto('/room'); + + // Check that we are logged in + await expect(page.getByRole('button', { name: 'User avatar' })).toBeVisible(); + + // Sign out + await page.getByRole('button', { name: 'User avatar' }).click(); + await page.getByRole('menuitem', { name: 'Sign Out' }).click(); + + // Assert redirection to login + await expect(page).toHaveURL('/auth/login'); + await expect(page.getByRole('button', { name: 'Sign In' })).toBeVisible(); + }); + + test('should sign in successfully', async ({ page, context }) => { + // 1. Get User 1's credentials from the auth setup + // Note: This is a simplified way. In a real setup, you'd pull this + // from the `global.setup.ts` state or a shared file. + // For this example, we'll read the email from the storage state (which is not ideal, but works) + // A better way is to write the email/password to a .env file from global.setup. + + // For simplicity, we'll just log out and log back in + await page.goto('/room'); + const userAvatar = page.getByRole('button', { name: 'User avatar' }); + await expect(userAvatar).toBeVisible(); + + // Get email + await userAvatar.click(); + const userEmail = await page.locator('p.text-xs.leading-none.text-muted-foreground').textContent(); + await page.keyboard.press('Escape'); // Close dropdown + + // Sign out + await userAvatar.click(); + await page.getByRole('menuitem', { name: 'Sign Out' }).click(); + await expect(page).toHaveURL('/auth/login'); + + // Sign back in + await page.getByLabel('Email').fill(userEmail!); + await page.getByLabel('Password').fill('password123'); + await page.getByRole('button', { name: 'Sign in with Email' }).click(); + + // Assert successful login + await expect(page).toHaveURL('/room'); + await expect(userAvatar).toBeVisible(); + }); + +}); \ No newline at end of file diff --git a/tests/fixtures/test-avatar.png b/tests/fixtures/test-avatar.png new file mode 100644 index 0000000000000000000000000000000000000000..b0f46a527826780e3cc86c34671ae934d3cc3ee0 GIT binary patch literal 117519 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 z zqi~FO4A9O&>yZy|!Wj~zj9$(nevy>vQurCDW55wFyadQ~Lp4WUSY4l)HY*rY0ZlY0 zX#DyK#Dg`0%G*J{tz7^Za}>0&GIW|Dc|+QJ?)Y~jUVZ291sjw9T4Ly!eHl=y%>=#$ zuGK%`hk(8PUqloBeykfKmH0zgOh?2MFqE+U4|s#$g&uhyybiK$W_k7y@9IXkE)ta5 zGi_cKgy?%js;Ac*uIw>rUX~>`w8n@ZEp>#XaTIzNZr-Szf|~XY4Z6uP`pw>Cymfd7 zl~;nV!fl_2?kGx4&iT!>OAed(;pP=4ZuIZ>l-`Mr_>x2&Lof%(E(T~p-qS>5N-vZT zk%`a>SUnxibayYF7w+-yogm_B?$&&-^`#voi=)H}`~8Z3|E=xUbv0!^b6u_G;TpDd zcIBVW{4)Rp*=+W<2B*8bVbcDULkUxqzqW)IY`14qYFpzBrnFr2fMRWY3y=s`rsRD8bhairmNDeA<;=Wd5u7l_xinVzJ~IDiZuV*^AHeLb7}1j zkDXW_G@##WJf^WC7YUrwu;;SHc{!X|PXa@BaRJHYNJ}v_k+~d*k23yJCH?UoEi_YMLl0M$ z^L(4Fgosh5t`8^*=-Z2Rb=!Gij^9Y6F1F}epO^eW;tFOU4$}|jY;4YRU}V*O9Ea=n zTwj6tXQf`Qm+nUcx`3;#wm_jgH=L_Wb4e$yQc<+b}S(i69GXr~C8__nP%Nd9ggjx$}7}YB5U|)UN)5V6gnbr3N4m zzPcNEbFXGp_u0MmeVVIbV4-93PPM zxk@%r#l;5SpQ<*Zsdc8IQZ!s;H`0vVEUffaz(|7&a7IR_E*X+3@70{0FJ4&+XQYTP zVx}NjB$0bM204gz$*`(QBhJtZ$lk5AfR@f73{>CTQ%kuGo#Dap4#6E;=+AU| zz701ATGN_oYYEb=sNG?~jt8-otPQ<~>!O1NQlR}IJ#r)Kr#*z_lVI}rsP;h3u|m{p z@EtvKaA#b2D<@ua>1>Y@U#gY*_~-o@x`#ZsU$6hEg%HaRs28B6$0IY?N5>_q6g>(u zKjL_{gU*7S7B0mUhRNHFZdqKNt7U0D|B*p+EE?=^wyhu+02)VJ^Fyj(YeVhJ9Asl! z%ks@XC9su3sAFmN6+i9MTt;Q_;RdIwa9^--+cU^_)y1)e=#80kG3Cbi=67m7x+km4 zRWzNnT`-(wk{WG5wRoy)9P#Sj^OrJDYGkK-uz>T^tP_7fkrYUvDbKSB;%TYj5|_?i zIItdTWb8ClTaSqzKRfhjKgC*PL#d=6CuMMd9>xi0IH^3Q-UwsRdt&^TEB&!Zy?y+S zQn!w*&!^w<;y;9^{~uO0xzfmFu%i(E9{6=DMN|u@V-FPF4CQz-J*`2EdND_nDcsvj zT>6ER3;22H@U&AiA<6YS-_Js2`6KrDL;F}*AbKx6i=_!m6Di^B!lYJ1((mm#Ns<*w z0*F|?`a~BdijP@>;xEyz9{)aGUZLNOrwnr@aGglew*9p#p+CjDCJcY`mWqXf`xxu{F-R zoyDU6ObVz;B+v7kRqqqPb}n8H&#vuwZd|FY<@iXH>UnhYoLu72cRLxtP7M5PhT}jC zgeMw@G}OX$9XZkYJCn;Cc51ut0B2uBq~4&FHwu-zFzHkE+Cr;or*g_b?hs=ki2N9u$jjB9c+i1GH6Fs@r2L!KX^62 zCB=6M*WeYp!z4|lov^ZhAByEVuozHb#OrXRG zzsNKG>U|vz3vgqnK@4&a*t3MDZGZS?jGUKSU72?_( zK;iuk5S*92qM3jTQy}g*H@i$`Jjso#+#sh+vDll_wt(yWk_d7a^f>~v)co!tWh)C% zzRUo$kwm+V!4fE(aFK$~BTyO`MVDzy=_e*~+GTggX@DEx`wyF&X!Udt5MaLpZ62Pq zuA)Qu#Jo`s(9`4mdjj~c&+?(T=<`&0+A}RbkNfhvHkM;$hOO{FP%M$ zYVUFdFh9|q=ET`xk=s*C4dN)eDe*?H1X}Advuw$_s4*8eG|c;9?^@9>;yFYN3qqqJ zg74$yo?uXsUXwMy(WZTrnNM-v3P4`P>`4_=M}t-6yql13u#j_X-n>jS0}Bj-=|f|@ zz4K6IgKNr>r^~qCxT7F^E-p9);Lfu!Dc-}-AwL|aKb@VVJf9j1TKR*dAW~$L3Dt{7 zuEVIoR3G(>qwkLaa4;I~MzK{VgG^q}0h9FkE7*AyX1UX+aWA~aER)T!Rwj0J3RhRJ z@K&L{SK>7Ej33^vDo*om%Nb~!yE&ud7~WTO!821J1Ea-DJb(kb!dtZSe#k2iRUuL} z7B+3?;Uii1`fv3b7Bo6@H0yu`tskJW>GlT+3xOtOLKRDyh;M3tD|wCg*X7lrfDYa@ z(z9rvQ%9MOUqg7Ypryh$L|S~Xi*wiX zPF93f@BneiReYz0=sz4)LNkT6waV4^eTVD^7SSj1MzW~UKR@c3P+n;loAsM#x z?*2iN+DgSb{wB4KBi;JP1MvI{p%xe~_LxYKSdM>V?g+YeIEtPT=mOWdjoUWG2kGCT zP(c_ppSnn3el#IBgL%eXCA6)dG>&`orz}X!Z4cb91R2?JrJcUu*3i$-e+w(EgW|~t zt*YKWoXalV11L?0Jv;2bhvnFXo-2aY=C|Nq+0YtSjjAg?#teK5(3WB$KPzE)8QR!t zJiq{%9M0CI;drc9sc2N@l)N&iHy6fSs6sz*C(p@zGR#mfL1Uc+*#riVYItt)x2?C; zESJFV4EoV<8jV_q!w(UsK_lKLP0D!zA@dz&?yE8d^nCd{6)#uJ+uYb-fmpuvR;ppUv3v{rMz@6dDR>K4#ERa|7>;aWOKKa?t|vRgK-=!QNVIs~ zgPAOQcbqq_D*VK`Y>?U46VG7t>CWp&K+Kv7=q*)s_?(i5i94IqKYnHTyjIH!OV62ptPNXYgmd>ae?#M77k=;b{~zv$zx|BhXq}IA zUU9A>FyBbVQ$+JN3fY{p^QG_lZBm)u0a2vIOt*Zk+1p+|>0itHjM_;T z-P_K^2E44k>}Tt>&-@xa*LahZ+&%VlQuOB9ZdNjtw$qFa56g+RZA4v7%vr3N$d~?= z1ldwb;ey5;#hwDkhEEBqVmDWD3KQ-2NBaV0ASy8DG|Oj_@@6xYU#}Z4|L&efpQuH z+{?JR3gX6z#%|3iy-L88opaPT=fD^Dw@=IC`j8tq;Zjw`+3B1eh(CXF%; zdHrWCJ*dM-LY1_E;~Xd-Q58pEr0&<(G^be__)^itrP|6>5qB)8LBfL8+xP9~Ry*S? z3)=g%b!s0ep6+6xHYVV0F1g~{;cF|}&k(iwIWH9YV-cx=7dd<6x3|b$Db#$sY`~FJ z*f~bh)26Cn=dAZ#yEO53IQv`V$8MXXO$$HYZ)X7H^i>cwlT@ahYHKM9is#wWX2b&B zVL%h|bt@NJkWddT&ui}qz9^6TVTUIlufu?l?;+|1 z??O3?St#?|l>j)(@)o)To!@UHsaOybVEp#LAi2*0utX6VVt5*IS6DpDDx8u$B>)i+ zZQ%4(2mL?bfBx@@m%dI#>1#GuW)_~GxdW1)x4Cx91@4-K>ZB~u^S!!$U1rnK?QpG< z&);FvMv`CGR?))QL9IGMO5lihScNhEzo}{br)iu2aThYK^&)fz$ z#DU=28fkwRz-?S$X76Qme^a8FLb3f$NBG%@kJ_gRF)aC#TbLrly3l8QlSSo__)0I` zyk>Q8oBaz1fY}H7`U&ydeQgwoi^e~_Z5l>fS@1z#n!fr`pSNz?HnM>HZ|hsD0-7>P zJAivCs=M(x?x^nwLFaUwHeW1rvWv8HyczdE_krAnwr&1(Oar@V){*bGCSaQX%1=6a zDOurOxjh~PBMyNuPfzTeGeKy@50=sFC`|ljMaaMeH=C#Q_CxiTn<1?fSY({M3KnFM zZi-c`@-KHvz)VP0eq^Tp)_{I$AI41jOv9qhCr9f$KlJsZF{e<_~J?rX();z1s>iRdo(*Zt>oPC7r%Wfx;pLv;* z(rk6^)Xs(s8+|h+=Ng-8fStGs@Y8?29XuVcPG=9)ZWyiMmzKD8ElEVz*_*D9p(vn~ zm+X2Fm08Z4XKf@<(e)MgE1(dU$C;n``g&N(hZkj3+a#^Wv zdi~wAU6#Rb@^8(-GdpP7^Ml916Ib;#ymwoS)-ingnM{tQj9Wj0DS&|_E%Ac65aCWE zp|pT$PB0E8f5z+IcRmH?m0o#Y8aRYprZeyeSU0 zP(yDn+CvG#4#W~OvrHbNc-?7PALIK@=#VY5J<}!sd~9-^>+K2`$B!$>^v+*T*Y=*k zkX17P1z&7k_+&smuqK%Rj6tCu=NI&#X{gKa#nQ&GNHuG$TnBvGqlokj`rz8lHPX_y zZQ6GV7y@$cx)5B^YF8Bp_Fk3tE6%(ODChp5+D-h zZfr<-zRD$8w?INQ_w|<^2u)ab;#JZW_qiX7YAcaiDl6{Lfx%DRe6sxEYqk-RvxL0( z*d<_C{=4A%>(rWYb%6okuKz(2j=D7569t&`ZKOI~08B^3=H3T@?g$Tto-c{)LD>i< zz+GB7^C;02aIcRVxJ|-cy2LXb`SORrvX!xQwy01euKbkwrYaBKeYCZAsVq7>0>6TE zEnE-nt>BdV@<=bmPMRbj?F6_TcJ?q)XgYE5O#jjVT|7VQBy~`G;#$7qXdB;R@*zR@XNewEQ;kNGy1`L}b8T~?)O=UzO!b()3sUkOt>@a>Tv@6C# zJnrhcuj#-RZSrgPw^uiJ-Afeu7>_}12t0Rdq_-w->zyr^q6Wb2sX!$1W~`5RA?4J@ ze%NW+S?vNkH5^8dwyD6($62DcG$p8R`aHQZ8tvI0d~lPpqEF!f3Xc5Ys1Hj@75iOP z?4m2vk`*?~`>~3Ac!A#p$BJPu>}P_`_{iz01YJQ)p+@Z#%`f# z)=Oop8JvdK#jkzHB;x$R*NM?z5bOZ1{&smoJpK7IdqJgOUDV@~V8EL6@=V7l$_6GW zU`8vkI)J>Si-R(+8^8zU*4g&solnI_cY~)aUEF0g>G~!V9!L;ojkR&kTmGoEj7UHQ zdH}8hr2^PA)rvRqMq6^tJA6O2vQEOZgEg1#i+4N)nhAtBO@27*p)62$qL>vHbN(-A z??3f@N&fBmq@#_<$A2mq{(Tnm?7CQJoay6xpOk=FS@_fN<3h@-ETk2C%GZ+&`?n^` zL$2&3P~|4HY-YMyWbi)qD3p+x<3=fHM7fxkQt%BBu99}s)?Iv+n03US$GwEUe*G$E znfZjzwNE8&$!zM3>v|mv7a+7;b&=4RZF}C}eGv;gTLwJ~B>*(_pAt6)5=b1(^E@$_ zg*fE1JI{wXgig=+bbX#g+}T$Y7Q1tf0XL!}Y67Vkha)Xm@Lt_}DzAO{-2G z#&2Q&AOWfmEE_sH)igCUg20o-p>JX|?6c5`+UX#`jr&e1@Vlp9*R-0`M)v2;G>eS$ z-s^<1Gka4Oz7Hxr)j{&%$IpZST*$dCSFjLfyk_7;JAM=Q>0uC4!BmpXYeeHr>NM)< zxBAl>euM9p-%)9@;It{iXTPJ$$reXBf6yTD9+A7^;q^TQ^LA%*aBZ z{^#N3l*TA9Jvp3=ENXkO3+sI5qh@->gjORk`hJ}rjCaJ6c1Q)^te4ZPVdrffchdE7 z_`#)ug9=ZqoUNOIX?=yrtmEHvc6%2vK*uirf^=NfBi4i&i_Gikl()Wnv+vLa(t{TRlBmv zk85s?{$9K)i$}MZ31gl7hBvw>TokvzGx$k?zcMVtu5o&651QR?y7?qX2zj(=m0+Bp z!F7Pz~=^aEwI-z%n^bP_73eu$4&_eIMO7A`NPNW4$;`jLO{hsga zbH_g4xp&-g?;nhel;`&(dB|_AIoDis(H04fB%9eWVoscldPjRe_pU~I0be*xXat7E zGy7ySFM9Px{VII5=Ei6Mnk_LWdmYy8Rb3fnWKw0mjW#^B{`gfNVvWg|ik+@}aXTK1 zZok*>iQg>1*ijft)-5r)J_zDP|>*&6^vOuF@$d&cqSo1h=xo#$x77j!W ztVDmY+>_h9$)%dvrMn@H8gq|p1Yp8x{lWSW_p8=l5=He3^Iw3=4FpfW1QdV<0${tN z_&g}=)zrvZj$bRt5L1$33yjBW-O8J*2;bt3V_;jYS?M8;gi3hDMr@Cr^*ezo&uHh` z9z~I{sD9n^rJHYJjkshtpEZ;!4{2U*{kE~3b}Mv^Enr=NXMi#UtH`cdoi zcvbZw@;8>IvPh>FeOB4Z^R|GFr7dvbJP~d^yrudtbDMU=&#L_2SU@VSRefTYQg&P+ zt9^8crnt923OXhMH5$SyG#vmfX}n3sqRUvPcG+(%cN#!4F%sF4Fy0)y3W(?u5M1lZ zOO-pNUp-Yt_6d{bM;O>z|8z^t1Jf3uje<`eINWn19YmavMh$bZe_2{@J$!Q z^d6Xr#|@=^$wyl;3w{eyPO{1A*pfw12^HDi`<~ zeIid9G5FD|vDiDnwJ?4*Y0}-W_-csXG^;!+#<84kGTWpb54cP|7?-Y=0U`O!h%a1_MrZu8i)5-a*{K0)IvY3a zvuP`%Ux_{gJ}}b```WIWNkeqF5QJ#=IRClsdX;6e)6gdl%5&hSO}%-xzYl%5ENuqy zSg~LeZM#!3YEgf^mDKsR9>KE-i#{JSaGbn{Iaa=g6z!L-`WFGP9;OrOdX9wTSO!KFqX>F^cR2Fv&UUnw|+ ztJZ5&Jg+sWUC&3tju|Ujn!r^F-yH}Z{&n7lX7KB(y77xOyOy8z5i;u^LAeZfVJxbMvVjyt=np5c>BE&g|0y6D7pkZvM;QW z-UWjkDRSfE!tc8>K7YYU_hN^(`I}S&3%`Mqg2Eu?c2B6xiLUJqyW{f?5?{O`68Bv< zA%NeZ_i0Eo+05Ab=4o51Rkm~806>Pe*c@Qf`uCk+e?8azZ=*3bODFD^xQWRkE6)Fi3Vo`Ma64QH*% z-VfQBnmK2J0;UUSNB_*gdCkizr^hUL$9yeme+c%z$n%azw`I8flO^uq zW@4G8?=!QTdNl6kDtVvd8gk|7s~}&T9SrvGochTN}NG6<%Mi{H99)yKCOjgDq1O*@m#=GrY`j( zz9dZ%`0Wh3GrWo@8#0XVO4zgMDJp=jDYR-PZ_G7$wv2xHpVsWiLf46LJlx?#v8)Mj zJCaW0i4VBK^T7=RpW>pTI|GlNmkw}dbG%f1>t?CUmMuktYy@)UbfhbMooB`W+y=O_ zBJyPV&GH<*D7IlZ1zXGM88o7W7w9k$17a?xfGW`^39~$2(1e7j{$?oMTH6=187unp z8)q5c7Ly*mvb$s7(K+5qI+w?ni>G_5?2}VmAf_v5Lv{K9T**7^Lx9I@b~k6DIa2}S zFG86JOo$Y#&t0T9B4%3N$9#>D&&c9!t5phNGAHhfsyt>BB*8u8KHz0c!q?$d?M@+u zg;2imcZbc3(;3N3H9mi?MZ!BHgA?_6J8Qdjmy$?g5J4Wp$(sN;QYSCNP{f>hM%rTS zi>Q(xEosORMZ%YX5NG@4C*Te?A%>Kl8e?UTMRT3=s7B^{LYf@w(#&fB)zwMIhT4#%pY-@7B z!BwienYVS7KIoZ*rqmKzbq+!VX_v#?wyAkI;tG#!zPC18ST*TVobco(DVX^Uq^)@J z3^K2=Cft0dsm6q)iGwd4TRua#HpbYSa;_8S;Dr#9lRqv!ZPx%hW?cS2z6^gukfEKm zWy`HotfYlK|1x6?n8-m<39ep8gc9b z#10C0dYlbeX>3;pg}#1=aPMvE!*SWbQT7BZokw+kN^6BkNW0j2y4;}#3bM-3qLrU% ztf#kp++2IQ&M7D1k8}s-U=jN*0MZ`R(?(kilXb#u{x>o~QEioal8VQvkmxCn&{X;A|T>XxA z2Q8<$ACa215Xb9goyQH9E^}~WiC(Jf_pPizx*Q9KQk{$h#*R+|S!%q^xDSx0-yuOp z!dF6!g%A?+a>1o#)wjd5s+DMdBpiWL`7UCfiu(z!C#*9b#ou&u&*1aW04p=e_^HMs zIaj(}8$2Q9608HA1f=?lhbDQvxFG$ZRLs3VgxWbpbLi`n+>(*#Wv$X_nmxk)k@5I` zeqX!G=q;rE@e#;y2igaW!Qsg0jx5fAK`f9T#aoI6=2R5ZXYV?-=5Zx91BucnAN#r~ zLUe0{UG=U)pOP}`C?kscSw>gN*azL@M)}b1Yt#Kz7x6Y>r86#-%&!uX--)^It7;Qv zeF;$)z}v9_O28&Hp*-*$oKFs%CBJiD#l^$Dw=4d$T#C%+7lqW3hL#60oP?y;<- zr-G%rettMZRLWFw&K$3-Rrd7+<+r5TiNPP? zMgW%Mu_&C2VqofHK!DGkQPd`wj^%7DDy+oirm!|c#*uskuy`Lm2&W)Lc(@3{RRi;X9SOLYSb)I=gi& z13Qhe#?F#)x9qep3|8gJqh7@h%|v@tVeYXYX1a+1JD)PTd(*C*cGYQ8cpW(CIVwQ5 zzIJLNG-ezu(TK0Qme=BiaTm>x>6%0qfs!)f#ylF~pa>L%TOFCvHQyVS6B)Lk)ta=C zd1iFrh}02JVTVe~$mns=^3;ph0GpJ0F!u{Az((;sNyHflpt?XLeysh}NN{^f&5w9m zZj5vUDZq|1SYu#kU|_<@YI2KIdOx3!Db$LdA}B6Z_xrNp_5?xD2IN z?~z#Hn$0wlaSq{Y1-N3)k3^n}Rv!{5Hcz|at1!XJYdv$pKMI&<_C(r&rvHo%Uj_}r z(pR4F@a(s1LRfX!d+@eNc_V6@Vo_mYng4zG*qqb znG7X2Q_pDc31#UB$LgFW4=HWM0;ysWsa z%2yulB)O$P<_`nrRBf^4VmB9ki3`y-w|-tj9<9vJn=E4EJ-^cAFvGCr%9%QX!d+<$ z3~p+kLSHyM>s2j0-#4NN7gQ3r6I?PltV~}tr|0`padv%kp1oRaUI#9kRV79G_3%6$ zA<518e82F;-D9&o-|K0*kSu4oL8A@L{*M}TTN|(3d+&rd6M3m!GZz>0!H8c8M}@1k zq^MfL^H)M6_h+>n62W)S46nM)Z=jsplM>MvN7(hc4e*Ij>+yv4 z;FvVf^H-R(qb*P{TzNBQ58XHf#8`$|Al+`nT2ab!0M;f&z~iL2k= zZ7)~NDaHeKr_{?yTQnqPWG^2MS$Zz%jql~jvGs+;EQBcgezXYglr@~P(a+aRu$~^f zy!pwgkyetOB^#0M4V84tteM+2Z+Qpc7yB~*<|1=N+j^W8 zWNra4e)yFzxq^&LI*Xn-_V- zXV*A2l$Lj{b1scD;)cb1CRDSG0Z@at`+vqX{|WPOIR9G5f$A&X2CxavUq^N!ECM7X z*N1tkk4Y?3{F>i6V`2*yn0N3gneXD_;udVx>=Z@i;&aH%WeKK7UGe?lM&9rR&_Cdh8-!%XYNy9`*}T|s+VPvlxj^(t$FnjpD*;pJ6w#q$c5?Tzg0 z=C~5S92a8Fuj6EVRY)VMqUb-DZ8Rz^ql{a_dnM@bj~Nf{cez*7q5`~ z>jmnP27u*Z%d0vCvya?4Ae}(7hX*QLJtz$z4=+VNh7%NitcdNRr`5r+rMAzZ7nEA| zl3z*n(>j|3gmpZHxz^5+XUcx=C?W-Om`=w8h=yOr?;A1@gSTz@uHKgzZaWBK#d9TO z6v&XVP>^OgzJuZ#0xI4}W^lKi6h-nI1Vb>)F7rK;#EE?GsC^i8tD_|#miz;(ri*1S z1vNIzP3D14SS#Y%gJbAHOf|=1Tx%qI|FSG{9tF4wr&(u?jglg3s-=*$FP`lJ|ZtdQ!Rq4 zZs-p+&IOS{q-}+-HQUGpKUlENx?QGp*C?NfcKwvhADAC~=h1s$c%%#^8%&8T=M+*L zBKI51RUs3vpe$AfX$M~+5nf{Jxc*tNoTTql1=-=WDGIzWL&BcF@T2pHUu@;NC>7Sl zrmuJ4V$=Ta;$-6aBmcP%KX}74k?c$)1oRFPEyZ4^`{QJ|IZooJX|i+zXNNYUPY3cF zi>M8m@Gh>U6pFO&_2Z}27=CuP?N3ST^3x_o_dw7n?X`>Cov@@)i(cJqvTNK`z_2+t zy~=ZQAM{}3MGkXfzh8XbUzXg$*T{vnE)f(SWucCIRjpUzAT77BgJ7ATzqHT|@yX1E z0`gV9HNBx9-niit`|b@BD~sNy+_1}Tub&lbbQJBMU(A!`mPO!f+HwETed}Gsj^%by z#`$gWQT^k)E8phsr`Q=gn=m0pIsuQvl^)Q8wS+dWyfiAimX9R?eHK)`$BGHadl$zc zD<^LlC_TztfR@fkK|5^u`=bjv$+^Y7@GQW{l!X^<2@r7KFixJOZ$OZ>^%kcS; z&zuMdBYD>C-oOZcWWDFg70CCcUKeS!+(tYNYck4P(0`G9BBpP|M40PDuwIVSx@K(t z#>gvuQ=w*M(M$8B+UjOB;8l}quCA_b>Ci24f-w1CQJfTxXKjF!S;g6lW%jA|$+G(` z>YHYwG|lB8L$x1w5wd0Z>U9EFN{Lac_S<>5`v%556LyCO!Do-{>`hH!>om>_wD&fU zzP*4-B}a;LjOSpLIylRK;|J3%p*FBe8li<%?K5PpW`Ovd*@F%dN;zz_$9dk49b1IZ z(}u(e#kgoV?I%j~T`uo#{SUVW4+}cxw-#||@~L`d07FN8?h?k19ourp z)+7IArSkKm_womup^5wjZL3sIv@fpH#^N90+S-u z5pkgPd9#M9a3k62Y0P01Q>G}@#dU@;Y!sOxT@k5d6PC!s{K;JLzWs$%{z|Ff6%W4T zE8n**%qQmoRy~%eMykhDI%=PmdubDnr^^pqX<|tY=dCVlX&C7o+ds#AO5e(wIATNa zX4}|6+JH?oo+P3(8hJY5Q}<^igv?kX6-ZceK67yHy(9uPIbNTaIgO|{ zAb5b(Y;o-!?$)+d0^kOeGXb^tk@LJ0eZKGe_51oK-Izehk!_=owOMnER^BQL;HomS zIFGs5U70^?n{0q0HJ@ppY-O;fx0}WEb%}8IWht&tUP8N6=UY2owYM@f8ClSp^W-&% zI3zJ2>)nOVNtHIVeyHBx&v>}pZsz-kA!c?UFv}c)Ot+8$V4@85G*EF`f^J=j%W_TW zYlc$&yW#pdDG%fhmJwzn<>FUFOYg&j91OEZJq%NV)!+^-`?Hq}qr$EqE z_u#u6zNA96d0};e@@!%S@7?Hsj+Pfy`dH%g-kwJgqTpqmd$%f2-($d@_)I1CryD|eD3AhPtT$$p~Xm^!3Oli zcve}7VK%pn!Yi9bgUK)>8P-Q?ikb0_yM~1(F9G4w|21obZ)6%jCGx-lr3puH)ND4A z1(X=~RrUp|ym-OcEM?RfGLF}4Jx^-=C3|e)oTXHQ3eYy)3I6Z2P5*bF^Q7p7&Y7(p zACl#yLdY!l8sa&~ue+0;GzP85ekcA4shp#q+OO6|`pDSw$Y=+B|4?H!7soDZlcczV zGC&st=E|}3n2{EW>Tw`wxGZ_Mlj}Gwy@C&ms}OkkwBaVYGY)c=Nxk(mOegYyCRCV= zyP|{3ip?d{4y#3y60%}+*GBS8>#5vZ8E1FlD!s))up?=^#2dBsIK|3xQO3a0Nh7Xv zw=e4!MHZh^BO4acLBPM$s}tOd)RT(33Z&iYFsRF|2vqH&P^}~zPl`T6&#q8mPIgDv zqpq7X4%$!p?dRdgfD|mqf}#E1j_^SE2puVIteQzEh7eI!e{$KSopKy1q!i7dU2QkI zq~9`4pLxM}i5s}wY6vmQSYVJvXP2Y8d!wAM(7MQaGP(4L3%gDa+b=a@D>}w3I94*Y$TXT5#TR!U+R@)Kz zY&aZybc7 z(-y(-byBvS`g&uy?tMOt>^7XEi zL8+|gaf$A>qBMww-LssN^Gal-E)V2+Vw}63+2A@%AK~}yZi3^TrBnh>z1FE-;LsOt z`QF~q`-`9^dcKKw!F1a2rqv$V=szie}-&bWQ+f4WUC+Mq)1I zz%3j&T4MTA7<}PLxjy(R;HlKf5y#(Yt#={*2aU0X9zI&>;8Q7{A~4oT2=@` zwuC6x!TeV;7ggKRq5t*tab$I*mEc7~Uw|dWhWVcqXbqrw0$1w51uLYMH4eH$i`d*2 zfm^acw-~`lgAt{uTfll&(l>yTQ? z?Zs0AKhE)z5|^{OT6}ZIhqsuH_Y(x%IwZa z*7@}`VQ?3QWzw$HmEa$2*WtNTv$YYi6!TQJk>c9s!~DJ=-vhVx^YgO6?s?_v25+Mc zgW+4zQBC%A$taHeeoLTk%($WsBHbb9xB3VtQ&MUawqbTQ(pmV;0xA2k~`<=tL41Cj+;uMX(V^YQ8GlA;(cj zTn`b~33lf$sAG&(zpL!5|EDGm{YOoD8>oec?L1q=xLD21NHH}9^jx2}{8VxL^mnD& z$X$A(=j$d@$WQqmvoEI45AcuW+K&buQTmw(^4NC`N>a0j#&_s7vo{%I0vSKdfGm7n z;asWW1Xoeo$feK1-+TpBH57gf^M$*LGEVZ|AA3!}Z4o_tO$4DnaE&A|D1AQeTOmh% zr1NuyJ0^&JO608>QLGC{#TdA*7FFH?vx>t|tr+Lih z#*Zx%>tfNh3FIHr;8!)+ahD--svXT`D@F~|Y_;W$jhZmF5Al2hALzH~fLfW?u7uKB zskqkYCaf-+$^VHtF6|wr&%V(%ieO@O0q=Zgv2v{}b{9*GSFApEw|gsH3xm>0FT6MG z3^z*dK7}$%%_L_C1u!n&8_R7QonT1jmqEqRtzbI>c>m#xHjiBcx#I08WJ!un6iRKC zt1yvN%O8CBC_pbaG6d@k$CT{^I@y5nlEU#(tV6y+xOZThE)(qq+-*t;At)_M&1-u#(9Gg8@bgHr8 zOW>j=<7esB{$El!?Em3?oUM_Rg^-EeH@P2qyJMNhwT257s#$)}77=R`oR>AzD@zl*EtXTzCW`+cFm>HhbJM#y_-mhT^nov2569b-OfF>%NgGztv&q&*_iG=7ad zyIc75Ue_&3uL%5pw(0nK1eujq;;zP9<5G-WA{h%p97yvF^ZfsZ@9}^7S^nd7Dr#TM zc9rvQ-Kz0QPgH}QpQ3_#fVPhR-Ek>k`Qww61iGsAoh0e{Fl;8mhlN&*E%r%Rd__@i zOOBj0J792u@jSf3hp*u6)89eRVFIDMr@Q*iQF%0xO2PApa!gwLj`ZEu?7ge}i+*&J z3uxY^GrB;pL|-T7;Qf<9^@ZR%wY~tb)|q{gYMzW=S|_c61WpVw?Xl8_zZ%$G4m>HF zND7Z9R!%_=IDzG@r6{It&+S-$*@i2iMzFMLP7aGuQx*sVuHyE^TRaJ{i!+A_s)azC_olNbi z5DT-!(#HN;ye`E`{n;tH(%e z8ilH@4m-W}r*pDmeu;pXCp65}cjok)BD-CQr;l4PMreZjARhm)%WWbdmKT)laE?W^ z+@zHcc5L>Jn3>6J;UBlVCmAU5NtS*Z)}-s8(+wdi6R=0qB)* zXH9-7f4*d{lIvhpE?k^(9^?YlEWb}DN4qqNGY5D((p7J)4+142$rm`a&_`^8H#O&k z=(QH!@zS$zSxwc6c6mHq9KB2p>$w(Ihiu{JmI-g0+HkNQMz0){-Nnw^dB|E-(j|mH z6iAD27{6V)1w&cn&d$j^I(bx6|MRw5)Lr^II{tl`EB!xl9x5$io-?Z}eSeg!rjC+; zgt!G4QgB|9_E4q1Segz!Az$P~?*4HF(^pU(2C5bEFl(xJqA3$ajh90cAmXfO*1KS8Q!FmJwMw?cV3kng@}W_wIlNVbgY-8Q|VExRTKL5 z8~G#J*8)FYA>BckqTHOG`HRBa6tzwQ&Vfwvs^$I1Mj}zw`=Zqw8DA|T@u`eU#BoNz z##11|%*!Lma~gOvjS6hybsx!3OJwXI!1EA8rz>1^Odjlw@fJE6K7k9`<7B9w_GimD z!s+x`CXb^0De$6uPIob$Le21{C&Ml))dl3^ou5L4+9Xcu7)D?e8<#XQK0yk3l}ltkf;=C5H`jIR6$1y2Ph)I8 zPQv+CZIjNAdC14qF9bF`byxSWGJz?hm`4d4)&jt{T}0ZIPG7Dp{xf#4BK~2bD<$E9 zV?V$w5y&?{9$LGdSnC*n;E&3&b!@tzC4$gfj`L=8>8CYOC(eAkcfSN+t|UYMoTI^n z#7dY=G5%$oe$y-dzHED9(>64FD_cEJr>`1CgrGRv1nQiBP66wRP8P_1EpX1d-&tMG z(6iPD3|wED*YyrYQQ;kCm_SeSc14^#LbYJElV#Mp8schUZ&gHbXRct)fU1K?W&K?1n>5;b?zU5SNpBragfXmAlA$X&AR^xf^#R>2GguIDa z$A$T5)1yI1pU4z3f-Jgt7h@am8@Eao@nJRV;4Uc9>A^nf{>0pz$s*oiAl6}8E+&6E z)!bbq3`V15^zv|saTQA}Lq$A8!^k2`Fcl{UwP`2QfZ3CwEj+s%zBhSDWexCGMAJiRQlPp@(UJPuh^h z^#7|I&%fh@BtxFOipe>Zf4OSMTyY>{98Vxv9vu~r|1~2SYyD){-SlLY3n_i}rUD67Hr{H$*v|1Y$8h;bD9ERZf~2Zz52-h}b|_ z29`&v+6mC9CwNue{Jukju)AbGM3!NQJ~IZ7F#;3IwUM!~a`9Yso}t4E&Oo^EiXrAK z>}xj+%lGAjXB*^wRJe(3qPsmdXUn8;PxI3lTq?dL$|NfL(s;vZ4imh|j$-M(I%3YT%_@UjgY(?WyJ zY{IdELx=`6+?blmzr>3DwDlQlg;u#RA#hGn6TBV@-ii<$ltgRaO;jU&1^-4yS1SYP zRFWJh_TIh-_@lVbPG9EwI^--H-{U#EhljRSWXDOLmSCgnaW zCs3VWZkkDww8~N7|5cL4()(@+#0y)|U=0utTHv;MvMsLr!J<}wUPxd2gJ^aXB0a(U zU4^P$<+7W-nMQQyB+)F2OXRiIj8GkLc`Z&wcbVSR(~&n6kKn(86r(FWm*s!r`-kS+ zUVCeUqN^8_9)g7$Sek|$)Fs#%-PfmXbx~8_dC*<5wpy+n-WP#LT2cl#2})fv&w>Tg{gT{tp~hR(>dQ0>^;AR-ADk2b zYV^Zp_FoU;f$cXwY~Pgw*iDxYm!%n$yTarTUh%Xn*!TzQCLnvEYx^&KY687Nu0x|- zQVH-Lrx4amoD~0^AlN7cmhC0=7cP}Y`DCSDd0R|pWWG-MSw%U?CJ^Po;-r*5m@%^M?f<|>aHf_A( z2TblQaxWz3;;fe1VQ{oI%02*FX6} z(m=1=5Uho?`q7i|IX3n0C21H`x0f5hkF2Jvtn8SB89na4PY|64aQm*BpOQ3?f4rt= zALkDFavE&l?(a+$#kp}|oRRD7~a8+bpH-J7rJcO(b@cBG_2p^r}eTV2^_S z1G^L1368g8RKmUyP+#&U8{eq=HI8p;vLpy$lME3bZFRSl>;$SqEaTi}W6RPc(#-cH z^51au?L1qqFE-M*^V*spQETF#KOw@R3ENFL-d7p7-#`!*+kguVsZ8G_YCo11U1c!Y zHj)cFE5>)p-a3Fii0av^TR8OJ-&I2XGk6Df&GQ6#X$&#xGW+wBX3hJCww{(*cfi4a zGb42Ws1G$;a}gv>^Oo`m5%d^nt~sNsu0PBu!+mc7!lK2Og;vZsoPGR_v)37ZFq1pk zJl!n?30+>i8AP$%;rHFfUp}Mhx%K^m>ew(hccWoyGq)zoqi4y0$_-Pue`IYWpw}b= zv9a*=*dqHWLa$omov`O@ZwhFva360?@$zsV^R67E2E;Yu&|_Mo zU9T^KbEA0%nYI0N2L>XrN8b55t0MglV0AwFnKRWqJTCbkW4J)v=H@b3+vQR)rvRulC`x( z2`L%%Db^|iQ54yT*O&0F)z^ORmf)?F-Jb#sPC*afraUvrwD);@vv9+RvQ>=*-I9=m zb9^MR9+SJ^k#~-Bq{I#%VRPuTm`u5dKm`Dm3ImiY198jKk=0V!wFCu(VpyiSpJtP> znJCt;POe#J6DSN--&JpNp<*VcX1vk7b1jv@mUWI)T%L~yyJiC-m=@jteniawv1#Bx z32gq&YiZ*tuZ(GC#uI11&PWP)O|Tbh_%zjyY6EBgf99~NVp2Cb@9=ZF9bUvc^v3Q{ z(X;(teZ|y2{u2Lc<^za0P(XbmAQID+oDQ;SjWEWA-N@G7X1Xzu4`3m7r6xW{9AZ^} z@?CY3$D!jlNgT@64Y?yP%6I|YRg|CB{NPFyg=cR1SuTt0iUSb((xPK{Fr?PoaJIn1 z4<`4mlW$v-%YReCpFPoxiK{>Gq8CTpsNQvCO6Jvx^m_}ru|Z26_4>zj%%5kS0^Qp zpQfN556t!eWv$%v$bW-q4XIx8mTic+QJQ2li1dWNYflUK>|Y@1 zc6fGVPG0REppNp~{tqX6EhS*%)ttL1AXg9NRA7u9C>>K4hP@O$ra<~F|C}|Rmt{0} zpnJPYd&cO7=3-J=fg7pgofC8hQ@&j!zo4r+HvatLoir70fzS{#vcqw)cY9X0+>mi3 znR1?CHzuUPR6|A2cgtQ|ECJZo)dAjT&dZPV_8+{%akA}XSqcG z<3Y2`{;jkhuZ4`<*w3lxRWid3H{!-rAc;1}38jk42#(b`JLm6s*_1?*4q|50Fx4c0 zmftu&#$V@Q?dYKV_^A}u*7jGpoAjC%%59%<75^}KG%I)MF7oSQq*O%V(|$t}|MFrz z;nY&)aO~Rc>Fua911>K4GDbBp>r1>b4+y5US@6mZ7of~r_(w|)xyQG#YJPFxZ$hzq zkvG$KQ*<;Zd9qp?9r3QD-PjloqRxhSzho>>CzdvE4&{Lw>RDP>nZ|3I%(C?6^>T-> z?Kad&2aR!J5tms~O47}*Af6=1e3|NrQg}1WwIcn=ts+G0?(y-}wDMn|)b1}(n*SFl zT@aG{D^Q*Qf%4N|fzmWRamFKCWunvtT$gmra2-h5&a->0aiY(s12FIva6;C$T~HI3 zB~R~Bj&YH%@#Nv^=L#EkQm(NXbnb5PxutmLw>9e~lM^Zo2u#`jJR2 zre;2IADyOpCw51*wXm$QD@8fdvg>KgN}tIRNV#SCD*I1#G(}j4`6y@U(FwkZN&?&0 z$j_Xr z+O()$4X;_^3o6OV7u!O;3WXy@`knbpKc#xpN?WrudWUBiHMM`iCfdQyE{ORMoGq3_ z+yb&Wn|booyPpnet*FACX^UQbl&tjLNR9weo|_X^b8vQ@G+}8sJ!w^k*fRtPpfr$g=A*xS^tI_+C@cNKyX^tl%rU!X z5Z9;X|M{6oQV(Dhzu$01@odU+B&c`vf-J4Z>&J3j5#UanmX~G05pp!GmBN9mW&RxZ zl*;(edlLz7QWT7VjSz&p%n_F8SX zRnx=br7#x;(zVB@{54#Fi-NW@&??0>N#ta+;W5IF85=}53*8LpLxnDq_kYpft*tB^ zxUkqi1iSz5n*{l3ZZ_uYQMlq*r>IMT#+M*6GE8F9%{?l>_@RU<5fX zSu$2ZNXo&mY^d|bQaeIRGSy~mHktjfdO?Ma*oNut*Zx@S{TXfXFSe9q zX>dN`T@2SK^vq6snrlh;n!L8I78rM(egA>%nS5nvr4vQ;#&b&qXln3cPzyB1HyH&W=m{yHPLQIomLi>UlLk6zTPj-lLu~YV zmmF8yh^*EfJx4o$`@oR6;7L;If zJ%UM#RYaN5ojsf&5UA#EulQ<)D{UVlWLJpLh-I?;`{m`V??>8q$GB{xXXqQ zh=|{MQi}GsL)D=}doML})`ULBj%JBYMtnD#01~Du?mx=ZzyG=s6rmcrkwcLWml>}Z z@_eHm=Y6A6(i@WIqsF-cVNKJ6v=Aa?FQX40fzrzQQ&HC89(5eNPdyG(meZ8HOMa@g z^L`#^D!&1tz4(Aqa;L8X zN;z+LboWI$(AU_e`#~)*Zqxz)N&S)l+=Rqhd(>LQdrjpv7-++`=O3vrxuFn?M(2nm zlL(3jm~Y$^@8ItmB}bIlGLD6;mP;^^-*`MfBV)Mp>39nG(5ke05#EH*mIBKnOr5oLWv&vD(jC)jF9eq&dYk5BKqiaX)w=W8-PwBq*gUL z)Mi4RJgxB{cXkH%1-f1s*DrmNm0twT))5%Cu>%U3I8;vXn(9*JDuZeI`M_?KRK2%_ zxi`?FXQ2U;GwHnkaF(aH)KcndVO3xRntOXl8v|n4Hxh{y^c7oCST3f^3G;gy_Utic z_sBZA{-}Aj7NBNj6LS|VkCxwP|6=cY^Xcx#vrCjsj8eZ4zFlThqoaKeuWtMUFL>IA zZS~gbkY8}wP=_~78LqzAl3W!C*6@6pY{mWC+Q%fDbXe-D?^L}=0Ztia3m`n)Fdy;! z?jKj9hrwBf#Q`AC167}y$6zia1uzh^cC9Iizb0wYXR3jp{g+2{^x#IsZ(w8N@&j!< z{4{*5CT9ERMXK>*3C#eQ_DFs;!QPrPv9KI`i-f(VXFBPL z6A8%=hKE1lCnI54zioEg|I|VVS$Lz4qJ{t%j0}9X?~y~zb7X&ZMFkKavFt=xEUJ`r z*ksoywInK!1N0W#N}iVVTBEUdjZJSnFhA$ryBYjvs6!I11G}UcG(62Yv0K*o0WZJ3 zg;1vSOr8AX_kQ_$vS0;EPtO>JeL=$?u$cVyW@U3v;WYOc z1r0ja0-ff^r$6W|{u`@hYp*QNphw?A1pS+c5&qBG6scjR2KTlWLV+SM!Zr zek-^$`yXzAlmK682LJwTlXAPS)m_r;8k)Rmw*S->-7M*D5Dye_5xpM&U3<#^AJ1KU zTPUtL?9Vm$risP;4wum)S5C&VXi{ZQ1(WlutUGs#cXZRj!Ro`5{U)X7x6nE=^@zP| z3}14>g5pwr##P+Gn)k0C3T&QwN@=qw&V6NPJEz*nhxat?c=~$ZXnREuQ7~H6A8Lc` z=Re{xe~f-%=mu|aK!)CPE`HwfVIsQJ&)i#hx#snNh&6}RH8u5gF?qIaL=f(6l3W!p zH=#%wvLw^5+C%$IGTzRDN$Ib06$P^g=>WIuQdQlbuT1Z~sL_#=gBY)>qF`67`s8ZN^=1-N@NzF3 z&W~-F%QFAift_G=61zJ7+u*lfjn-RyZT`Z5dNg}->vZ=9#)o;|5rCh3PS-zS_>Bbx z>XKhVwSjH+dgqED=+45AYaH3sS^#aX{f#zpMlEt((w~kU@CbG%v1bQpzAGXnsB<5I z{*f{Ph!yCy8>UdMosgK)J!FXunyovPjD=3uM~m1O78rCw+!=!$2r5MB10~rf6#p5K z8UIA&^;uwD4ELDJzVEYPEhPoBHDO+njF;kQE4F@1kz&GgnH5rLb8ucxrM%r{;H_o( zD+jq3k~UxLwAZ^5=Jkyid+$rve=Za7Rc*EJDB3*`;(0kwv;XZ46+x2iA^rFCJFfJ= z;tPKkj~Um5OGd^%y?(f|uO6Q_X}soV_jZMJN6aqib4jY*W|%2WQ~u~R+FOUs;)&ss zfsE&~NUeAITcR?@v5N8iKXV~ggqvN_M62ICrS@(}#~YyJmJO~gUs?Y7wk1aX87}M( z0XRk4tuOqI{7Y32Q;jP_BftO62pCf8H0aBlb5S#sY+KX!GK5JS7uqai3*Uk-bA6k$ zn+zT#dCBY9R@T_9FZG`ejXh8Q)BI&~=@vP?hOiC=py7DQ8Fvx0efFg~-?HP#RX5wev*;L1Q zeweTVl+5`K4#|%lfN13R_~a;qJaYS04SBFr86JAOLh{rw$X&pQz+12E-U5>YZCYV^ zUZiKro|3msYX1MK?z+O7+SV;9Y`Tb)07{~QR6!|`A|#>$0%8zQARtYU5;4*R0@;E{ zCv=o5y%UjMOaMhedXbKFkfwx9iAe}&={d*mUhh5k;Xd5*xE|)p`ZB*c<{bZ+WBhss z&6szuU+x@kRQLY%Vt_eb=UA>mVEQsEtL)1Fp_FS_4W6f+9Ur{BJRLdX69KZNr<=o?= z0R<=NP^Dd@&ZJCtG5jq;FmxP_(jTo zHtB<|VOzuNp>(@X8y_U{11(Kg`I z^yc%&7$7GbIsvQ9*Rs+|(S#6dPF87F#&rzYxheEAm@Mi$lTCoWp@B5*0#pNZGD#>u zQ+3kZqO}dFWE7*pm*JCO*z?HkEkXwk0_ZXlE)v9L)|V0o&t;Qt1>_d=c!<`10qmq^ z(bWjVaZ{g%B%wx>%sZ?i#Jujz4FUQD=~0weH5T`$tU4{#{A z|3<$5zb9V56S4oPXE!WgfZ&&D0kuP=WId}V4ujPa95>pIDC*PXWqsw-j`@;?iA({P zFhautM=<)sYLGHXr_zNk&}rSWF7dCrgm(egxO=22{CFI3CdUe}Pt#=a&m7uR`JTtO z5rnl~+>waT^LB}BvhWo7c9iRet5wz`ug61FDM*xO z*({uA6~;~yOD$hf;zAWUjj*~i_E#8|7l_1Y?9L1u9cE=brfuM)YO-ylQ=+iM17tP7 zv8wESkAmQ=Shsb_>wBb(FS}*vR|dFt z`If+;za4-Mb&@-^YEXjb<1Qq#X)SU7`F0Xquv2dS&-z~bFJ^?-tFL^vdFq8y@dfML zG&|pDz!mKdR_u2fXt%r;!JcE2c#Pq@`prutx200tDkZfGdj*{uToRvZNz4Z>9OJ$x zaPHFb$eP7s5T~9&^)t$n(a|e`-RU`lqMMTA+KJfW3HI+D5vScHMUSb<`mwEIZ1PNH zQ^dZ@*hjiVo|(r|l@Az{%ZwJPzzg4sK#_`m?D{hH;Sw{60#Z*~zY71gwo{J&_0hjJ z5BZLsoL|7~{dcAW|NG#x|90?|JsIXk&K-?;PH^jpo{nwUsbBB4sIHpZ-5NVzE{gKB z&aI5E_HUTW`$ZgqUvjW@rMLI$u43-ynrh~sBg#}&a8|fLu={CPBoFYtfUacF%jY<5 zhcxUJh#ngXSbq;6_VWYyLAApW-X*KP{vLSdNuKQ$+&j{(3FZ2_+VCYWQ(ge?TR>_v zCU@ujdGu-q_)Tlf<+-N#mb-Rg#Gi2Q2iV>)Mp?jxasv-`lqg0!`bJ3K9vrzKGkO<+ z=Inke%CIpxW*tOe(zjc_H6(MosV;VQlIOhml+|T~?gr*oo>>%Um}ck%{%hzw^Wd~1 zetiS<1C;sHuFg)uWO((Djw1O!)`{$8yxZ$`9Qb!i zZ{-%(N87aAqYoZxhZ-R^?>+DLl&;N?=a6?2Ngw$0`onLbgNOgGKgTAhCRzi{W|^~is|=Xuh&0}-yJ&PEL|}PXvGESB zm2mccW3}DY*Q^~~ua?D$UGhGRGu9>_Z&RnK<`|ah{g-sMHA`Xp_a$s6-pD^(c@!k2 z3OND7eB zrM-kpc;B}Rm94S6^H+z9n)|g4-ilWqp2|`h?WZUs8~Al|(h?j8XHuM4dvO&-F)-)U zk=K#US&WZE^yH|Bi$24@^QB}0wL#!kl@xi!Dfe^l;+Fu?!>#`7hd)TFgnhSW?ru>_ z6QXcTu|E@WgS9pwoSf*|>vw^=b6TeF?;A2-re79dFs-nd{F?kxz%=y) zoI5hJr%ECpC|J42k}`1iXzN<;Gv*(2rE+bIFCtU`No5MAQL@VaY8CY0mBc7JIOr9i zTs=%RLpSNVc=~0x7hoG)dAil#ewh{hc@9@4TmXeMd%@EGEMIMEoi7%0NA9E=Rem^U1f$tKh%yp4HT>EO7%uqjUs12axHPVvI>9H8Cn15bf^62llp6McClancwC;TySYP1U#VeyCJ(E5+n%E1LD^IRjp zn3d6cYkB51ps>9RVV+ql>pDb_>5P67B2Ml8$ko#$b(-teBSbbR7qi(U}!q>FQ zwv0p8a&LUO$@3}uedUmO@s?grjQghM@-sEWeOVc?6WQs?N#lswxQpxB3%)`-TkEZ+ zl4EP2hJA!IBrsYQgw%1NYtP6C;?4GRunrKOfF8?>$y&o%3M+#Ou=HXrVnaiO8sz>- zdq0Ttmh(P;`t_d;*@c}(vysWkOmE}}W`>Bz)t`PQHJ!jVX_E55K z&Mp9}nj$=EVOIzB!CJ*ERtWP%~rhh5W{1=J-|J!2%)Uhpy zfWtbQ)AK^)p4RDy!E6e-psGoLOMR5Ya` zngUm+y4;)BL?6Dn{iGe9ep!Pts6{x6VPuDOOZ*$xdI#xtfIN+RlNgp_oV1Iw;ryaC zdB@|sg;ISpn#|`L6@bRJNCccar$i7V`}@d&eWP^S?)HAR6uoWXEntfs!w(42h>#FU zjhON9l)4~AmW?yjT(*RWWMJ5DvK#u=v=p>gru z?l@~Mo9zO}a;U%UxVn~)Lu8tSw@mz~Kc|=F*}>A~LA{>V(N?&(RJ8H#5t;W0tZXu04@j!&Z_=eO}=A>D6WX{kPPWI`1=nGWgPz_Y! zTbof3oXK;U*|S>kc|6G|=Smp3i|wNOlC*UxCA*FL&!EJyU9XWUR`C%UkHXANBZblU zez7Bg>L(xh{pm|9875xIvdlQcmZ#)_fjTQ>DY1oj^>6Nl?jTrEUdZF%AdOOSt9yvD;6Yc0RH%jsjvkn&X3uc2q}FU+1S`}Q z(JHc=BBw=LmKEP*h^dz4)>^?nBd&az6W936K~Hcgw`r;gpZjc_!D^u9_O2yyC^C>` zCQzkY$0`J9S-vqwV%~Mj;8b~dq78i1kZ)U9_x=Q%LuO@1$J4FH40shO1eDT9Ri_UV zx!_KmvKKc$mnMzRy$3H}CY-s{epCKYK^X77Z~59cX3|XlqI|97xtPcEQqM%|+~z{M8?TXhuNdk1yzluYCmK6*YzSaogG9`3F8>{z#4_!VgnLg*CGxWzT8H?hp#H) zDKqex?C>@74@>v@f-Hd;vNyPW*Jm~KQ`mOc2)!Y5QIiki%%uLJe-!&6s&?PqYC+0Z zqFm=;H*f?m0vP;%8k2jdlV%7+Lb}dbQ8tqg7&M^dBfvfK_WM+NBa!~pF`LjELKn&u zS@??>4w28y1Ev&-X=dC0Hda6fqGtZYuAfIAFl61*be+deNdeh2R { + + setup('create user 1', async ({ page }) => { + await signUpAndConfirm(page); + await page.context().storageState({ path: USER_1_STATE }); + }); + + setup('create user 2', async ({ page }) => { + await signUpAndConfirm(page); + await page.context().storageState({ path: USER_2_STATE }); + }); + +}); \ No newline at end of file diff --git a/tests/profile.spec.ts b/tests/profile.spec.ts new file mode 100644 index 0000000..53b5fa6 --- /dev/null +++ b/tests/profile.spec.ts @@ -0,0 +1,49 @@ +import { test, expect } from '@playwright/test'; +import path from 'path'; +import { randomUUID } from 'crypto'; + +test.describe('Profile Management', () => { + + const testAvatarPath = path.join(__dirname, 'fixtures/test-avatar.png'); + const newUsername = `TestUser-${randomUUID().split('-')[0]}`; + + test.beforeEach(async ({ page }) => { + // All tests start authenticated (User 1) and on the profile page + await page.goto('/profile'); + }); + + test('should update username', async ({ page }) => { + // 1. Update username + await page.getByLabel('Username').fill(newUsername); + await page.getByRole('button', { name: 'Save Changes' }).click(); + + // 2. Check for success toast + await expect(page.locator('text=Profile updated successfully!')).toBeVisible(); + + // 3. Reload page to ensure persistence + await page.reload(); + + // 4. Assert new username is saved + await expect(page.getByLabel('Username')).toHaveValue(newUsername); + }); + + test('should upload a new avatar', async ({ page }) => { + // 1. Set up file input + const fileChooserPromise = page.waitForEvent('filechooser'); + await page.getByRole('button', { name: 'Choose Image' }).click(); + const fileChooser = await fileChooserPromise; + await fileChooser.setFiles(testAvatarPath); + + // 2. Wait for upload to complete + await expect(page.getByRole('button', { name: 'Uploading...' })).toBeVisible(); + await expect(page.locator('text=Avatar updated successfully!')).toBeVisible({ timeout: 10000 }); + + // 3. Get the new avatar URL + const avatarImg = page.locator('.h-24.w-24 img'); + const newAvatarSrc = await avatarImg.getAttribute('src'); + + // 4. Assert the new URL is from Supabase storage and not the placeholder + expect(newAvatarSrc).not.toContain('placeholder.svg'); + expect(newAvatarSrc).toContain('supabase.co'); + }); +}); \ No newline at end of file diff --git a/tests/transfer.spec.ts b/tests/transfer.spec.ts new file mode 100644 index 0000000..113773e --- /dev/null +++ b/tests/transfer.spec.ts @@ -0,0 +1,73 @@ +import { test, expect } from '@playwright/test'; +import path from 'path'; +import { USER_1_STATE, USER_2_STATE } from '../playwright.config'; + +// This test uses two authenticated browser contexts. + +test.describe('File Transfer (P2P)', () => { + + const testFilePath = path.join(__dirname, 'fixtures/test-file.txt'); + + test('should send and receive a file between two peers', async ({ browser }) => { + // --- Setup Sender (User 1) --- + const senderContext = await browser.newContext({ storageState: USER_1_STATE }); + const senderPage = await senderContext.newPage(); + + // --- Setup Receiver (User 2) --- + const receiverContext = await browser.newContext({ storageState: USER_2_STATE }); + const receiverPage = await receiverContext.newPage(); + + try { + // 1. (Sender) Go to room and create + await senderPage.goto('/room'); + await senderPage.getByRole('button', { name: 'Create New Room' }).click(); + await expect(senderPage.locator('text=Connected Room')).toBeVisible({ timeout: 10000 }); + + // 2. (Sender) Get Room ID and Receiver's username + const roomId = await senderPage.locator('.font-mono.text-3xl').textContent(); + expect(roomId).toBeTruthy(); + + await senderPage.getByRole('button', { name: 'User avatar' }).click(); + const senderUsername = await senderPage.locator('.text-sm.font-medium').textContent(); + await senderPage.keyboard.press('Escape'); + + await receiverPage.getByRole('button', { name: 'User avatar' }).click(); + const receiverUsername = await receiverPage.locator('.text-sm.font-medium').textContent(); + await receiverPage.keyboard.press('Escape'); + + // 3. (Receiver) Go to room and join + await receiverPage.goto('/room'); + await receiverPage.getByPlaceholder('Enter room ID').fill(roomId!); + await receiverPage.getByRole('button', { name: 'Join Room' }).click(); + + // 4. Wait for peers to connect (WebRTC can take a moment) + // Sender asserts Receiver is 'Live' + await expect(senderPage.locator(`div:has-text("${receiverUsername}")`).getByText('Live')).toBeVisible({ timeout: 20000 }); + // Receiver asserts Sender is 'Live' + await expect(receiverPage.locator(`div:has-text("${senderUsername}")`).getByText('Live')).toBeVisible({ timeout: 20000 }); + + // 5. (Sender) Select file, recipient, and send + await senderPage.locator('input[type="file"]').setInputFiles(testFilePath); + await expect(senderPage.locator('text=1 file selected')).toBeVisible(); + + await senderPage.locator('button[role="combobox"]').click(); + await senderPage.getByRole('option', { name: receiverUsername! }).click(); + await senderPage.getByRole('button', { name: 'Send' }).click(); + + // 6. (Sender) Assert send is complete + await expect(senderPage.locator('text=File sent')).toBeVisible({ timeout: 15000 }); + await expect(senderPage.locator('div[role="alert"] div:has-text("100%")')).toBeVisible(); + + // 7. (Receiver) Assert file is received + // The app shows a toast on success + await expect(receiverPage.locator('text=File received')).toBeVisible({ timeout: 15000 }); + await expect(receiverPage.locator('div[role="alert"] div:has-text("100%")')).toBeVisible(); + await expect(receiverPage.locator('div[role="alert"] div:has-text("test-file.txt")')).toBeVisible(); + + } finally { + // Clean up contexts + await senderContext.close(); + await receiverContext.close(); + } + }); +}); \ No newline at end of file diff --git a/tests/utils/testmail.helper.ts b/tests/utils/testmail.helper.ts new file mode 100644 index 0000000..e02b631 --- /dev/null +++ b/tests/utils/testmail.helper.ts @@ -0,0 +1,107 @@ +import { GraphQLClient } from '@testmail.app/graphql-request'; +import { randomUUID } from 'crypto'; + +// Create a GraphQL client for testmail.app +const testmailClient = new GraphQLClient('https://api.testmail.app/api/graphql', { + headers: { + Authorization: `Bearer ${process.env.TESTMAIL_API_KEY}`, + }, +}); + +const TESTMAIL_NAMESPACE = process.env.TESTMAIL_NAMESPACE; +if (!TESTMAIL_NAMESPACE) { + // Soft warning so local devs know why tests may fail + // eslint-disable-next-line no-console + console.warn('TESTMAIL_NAMESPACE is not set. Email-based tests will fail.'); +} +if (!process.env.TESTMAIL_API_KEY) { + // eslint-disable-next-line no-console + console.warn('TESTMAIL_API_KEY is not set. Email-based tests will fail.'); +} + +/** + * Generates a unique email address for testing. + * @returns An object with the tag and the full email address. + */ +export function generateTestEmail() { + const tag = `user-${randomUUID()}`; + const email = `${TESTMAIL_NAMESPACE}.${tag}@inbox.testmail.app`; + return { tag, email }; +} + +/** + * Polls testmail.app until an email with the specified tag is found. + * @param tag The unique tag for the email. + * @param timeout Max time to wait in ms. + * @returns The email object from testmail.app. + */ +export async function waitForEmail(tag: string, timeout = 30000): Promise { + const startTime = Date.now(); + + // GraphQL query to fetch latest emails for a namespace filtered by tag + // Note: Schema names may evolve; we access defensively below. + const QUERY = /* GraphQL */ ` + query FetchEmails($namespace: String!, $tag: String!) { + inbox(namespace: $namespace) { + messages(tag: $tag, limit: 5, order: DESC) { + id + subject + from + to + html + text + createdAt + } + } + } + `; + + while (Date.now() - startTime < timeout) { + try { + const data: any = await testmailClient.request(QUERY, { + namespace: TESTMAIL_NAMESPACE, + tag, + }); + + const emails: any[] = + data?.inbox?.messages || + data?.inbox?.emails || + data?.emails || + []; + + if (Array.isArray(emails) && emails.length > 0) { + return emails[0]; + } + } catch (error) { + // eslint-disable-next-line no-console + console.warn('testmail.app API error, retrying...', error instanceof Error ? error.message : error); + } + + await new Promise((resolve) => setTimeout(resolve, 2000)); // Wait 2s before retrying + } + throw new Error(`Email with tag "${tag}" not found within ${timeout}ms.`); +} + +/** + * Extracts the Supabase confirmation link from an email body. + * @param emailBody The HTML or text content of the email. + * @returns The confirmation URL. + */ +export function extractConfirmationLink(emailBody: string): string { + // Try to match an anchor href first + let regex = /href="(https?:\/\/[^"]*\/auth\/callback[^"]*)"/i; + let match = emailBody.match(regex); + + if (!match) { + // Fallback: match plain URL in the text + regex = /(https?:\/\/[^\s"']*\/auth\/callback[^\s"']*)/i; + match = emailBody.match(regex); + } + + if (match && match[1]) { + // The link might be HTML-escaped + return match[1].replace(/&/g, '&'); + } + + throw new Error('Could not extract confirmation link from email body.'); +} \ No newline at end of file From b06dfe417331656a744762e595305abc9e64fb19 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 5 Nov 2025 15:44:08 +0000 Subject: [PATCH 2/8] Initial plan From e6890ac89f96d63e2535cae63e300f2dfe4fb82c Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 5 Nov 2025 15:53:30 +0000 Subject: [PATCH 3/8] Add unit testing infrastructure and comprehensive tests Co-authored-by: jomzxc <74537369+jomzxc@users.noreply.github.com> --- package.json | 16 +- pnpm-lock.yaml | 1888 ++++++++++++++++++- tests/room.spec.ts | 99 + tests/setup.ts | 41 + tests/ui.spec.ts | 133 ++ tests/unit/lib/utils.test.ts | 39 + tests/unit/lib/webrtc/file-transfer.test.ts | 218 +++ vitest.config.ts | 38 + 8 files changed, 2452 insertions(+), 20 deletions(-) create mode 100644 tests/room.spec.ts create mode 100644 tests/setup.ts create mode 100644 tests/ui.spec.ts create mode 100644 tests/unit/lib/utils.test.ts create mode 100644 tests/unit/lib/webrtc/file-transfer.test.ts create mode 100644 vitest.config.ts diff --git a/package.json b/package.json index 970483c..a77fda9 100644 --- a/package.json +++ b/package.json @@ -7,9 +7,13 @@ "dev": "next dev", "lint": "eslint .", "start": "next start", + "test": "vitest", + "test:ui": "vitest --ui", + "test:coverage": "vitest --coverage", "test:e2e": "playwright test", "test:e2e:ui": "playwright test --ui", - "test:e2e:report": "playwright show-report" + "test:e2e:report": "playwright show-report", + "test:all": "vitest run && playwright test" }, "dependencies": { "@hookform/resolvers": "^3.10.0", @@ -68,13 +72,21 @@ "devDependencies": { "@playwright/test": "^1.45.3", "@tailwindcss/postcss": "^4.1.9", + "@testing-library/jest-dom": "^6.9.1", + "@testing-library/react": "^16.3.0", + "@testing-library/user-event": "^14.6.1", "@testmail.app/graphql-request": "^1.8.4", "@types/node": "^22", "@types/react": "^19", "@types/react-dom": "^19", + "@vitejs/plugin-react": "^5.1.0", + "@vitest/coverage-v8": "^4.0.7", + "@vitest/ui": "^4.0.7", + "jsdom": "^27.1.0", "postcss": "^8.5", "tailwindcss": "^4.1.9", "tw-animate-css": "1.3.3", - "typescript": "^5" + "typescript": "^5", + "vitest": "^4.0.7" } } \ No newline at end of file diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index a051128..3e36bad 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -100,7 +100,7 @@ importers: version: 2.78.0 '@vercel/analytics': specifier: latest - version: 1.5.0(next@16.0.0(@playwright/test@1.56.1)(react-dom@19.2.0(react@19.2.0))(react@19.2.0))(react@19.2.0) + version: 1.5.0(next@16.0.0(@babel/core@7.28.5)(@playwright/test@1.56.1)(react-dom@19.2.0(react@19.2.0))(react@19.2.0))(react@19.2.0) autoprefixer: specifier: ^10.4.20 version: 10.4.20(postcss@8.5.0) @@ -127,7 +127,7 @@ importers: version: 0.454.0(react@19.2.0) next: specifier: 16.0.0 - version: 16.0.0(@playwright/test@1.56.1)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) + version: 16.0.0(@babel/core@7.28.5)(@playwright/test@1.56.1)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) next-themes: specifier: latest version: 0.4.6(react-dom@19.2.0(react@19.2.0))(react@19.2.0) @@ -171,6 +171,15 @@ importers: '@tailwindcss/postcss': specifier: ^4.1.9 version: 4.1.9 + '@testing-library/jest-dom': + specifier: ^6.9.1 + version: 6.9.1 + '@testing-library/react': + specifier: ^16.3.0 + version: 16.3.0(@testing-library/dom@10.4.1)(@types/react-dom@19.0.0)(@types/react@19.0.0)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) + '@testing-library/user-event': + specifier: ^14.6.1 + version: 14.6.1(@testing-library/dom@10.4.1) '@testmail.app/graphql-request': specifier: ^1.8.4 version: 1.8.4 @@ -183,6 +192,18 @@ importers: '@types/react-dom': specifier: ^19 version: 19.0.0 + '@vitejs/plugin-react': + specifier: ^5.1.0 + version: 5.1.0(vite@7.2.0(@types/node@22.0.0)(jiti@2.6.1)(lightningcss@1.30.1)) + '@vitest/coverage-v8': + specifier: ^4.0.7 + version: 4.0.7(vitest@4.0.7) + '@vitest/ui': + specifier: ^4.0.7 + version: 4.0.7(vitest@4.0.7) + jsdom: + specifier: ^27.1.0 + version: 27.1.0 postcss: specifier: ^8.5 version: 8.5.0 @@ -195,9 +216,18 @@ importers: typescript: specifier: ^5 version: 5.0.2 + vitest: + specifier: ^4.0.7 + version: 4.0.7(@types/node@22.0.0)(@vitest/ui@4.0.7)(jiti@2.6.1)(jsdom@27.1.0)(lightningcss@1.30.1) packages: + '@acemir/cssom@0.9.19': + resolution: {integrity: sha512-Pp2gAQXPZ2o7lt4j0IMwNRXqQ3pagxtDj5wctL5U2Lz4oV0ocDNlkgx4DpxfyKav4S/bePuI+SMqcBSUHLy9kg==} + + '@adobe/css-tools@4.4.4': + resolution: {integrity: sha512-Elp+iwUx5rN5+Y8xLt5/GRoG20WGoDCQ/1Fb+1LiGtvwbDavuSk0jhD/eZdckHAuzcDzccnkv+rEjyWfRx18gg==} + '@alloc/quick-lru@5.2.0': resolution: {integrity: sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw==} engines: {node: '>=10'} @@ -206,16 +236,300 @@ packages: resolution: {integrity: sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==} engines: {node: '>=6.0.0'} + '@asamuzakjp/css-color@4.0.5': + resolution: {integrity: sha512-lMrXidNhPGsDjytDy11Vwlb6OIGrT3CmLg3VWNFyWkLWtijKl7xjvForlh8vuj0SHGjgl4qZEQzUmYTeQA2JFQ==} + + '@asamuzakjp/dom-selector@6.7.4': + resolution: {integrity: sha512-buQDjkm+wDPXd6c13534URWZqbz0RP5PAhXZ+LIoa5LgwInT9HVJvGIJivg75vi8I13CxDGdTnz+aY5YUJlIAA==} + + '@asamuzakjp/nwsapi@2.3.9': + resolution: {integrity: sha512-n8GuYSrI9bF7FFZ/SjhwevlHc8xaVlb/7HmHelnc/PZXBD2ZR49NnN9sMMuDdEGPeeRQ5d0hqlSlEpgCX3Wl0Q==} + + '@babel/code-frame@7.27.1': + resolution: {integrity: sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg==} + engines: {node: '>=6.9.0'} + + '@babel/compat-data@7.28.5': + resolution: {integrity: sha512-6uFXyCayocRbqhZOB+6XcuZbkMNimwfVGFji8CTZnCzOHVGvDqzvitu1re2AU5LROliz7eQPhB8CpAMvnx9EjA==} + engines: {node: '>=6.9.0'} + + '@babel/core@7.28.5': + resolution: {integrity: sha512-e7jT4DxYvIDLk1ZHmU/m/mB19rex9sv0c2ftBtjSBv+kVM/902eh0fINUzD7UwLLNR+jU585GxUJ8/EBfAM5fw==} + engines: {node: '>=6.9.0'} + + '@babel/generator@7.28.5': + resolution: {integrity: sha512-3EwLFhZ38J4VyIP6WNtt2kUdW9dokXA9Cr4IVIFHuCpZ3H8/YFOl5JjZHisrn1fATPBmKKqXzDFvh9fUwHz6CQ==} + engines: {node: '>=6.9.0'} + + '@babel/helper-compilation-targets@7.27.2': + resolution: {integrity: sha512-2+1thGUUWWjLTYTHZWK1n8Yga0ijBz1XAhUXcKy81rd5g6yh7hGqMp45v7cadSbEHc9G3OTv45SyneRN3ps4DQ==} + 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.27.1': + resolution: {integrity: sha512-0gSFWUPNXNopqtIPQvlD5WgXYI5GY2kP2cCvoT8kczjbfcfuIljTbcWrulD1CIPIX2gt1wghbDy08yE1p+/r3w==} + engines: {node: '>=6.9.0'} + + '@babel/helper-module-transforms@7.28.3': + resolution: {integrity: sha512-gytXUbs8k2sXS9PnQptz5o0QnpLL51SwASIORY6XaBKF88nsOT0Zw9szLqlSGQDP/4TljBAD5y98p2U1fqkdsw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0 + + '@babel/helper-plugin-utils@7.27.1': + resolution: {integrity: sha512-1gn1Up5YXka3YYAHGKpbideQ5Yjf1tDa9qYcgysz+cNCXukyLl6DjPXhD3VRwSb8c0J9tA4b2+rHEZtc6R0tlw==} + engines: {node: '>=6.9.0'} + + '@babel/helper-string-parser@7.27.1': + resolution: {integrity: sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==} + engines: {node: '>=6.9.0'} + + '@babel/helper-validator-identifier@7.28.5': + resolution: {integrity: sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==} + engines: {node: '>=6.9.0'} + + '@babel/helper-validator-option@7.27.1': + resolution: {integrity: sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg==} + engines: {node: '>=6.9.0'} + + '@babel/helpers@7.28.4': + resolution: {integrity: sha512-HFN59MmQXGHVyYadKLVumYsA9dBFun/ldYxipEjzA4196jpLZd8UjEEBLkbEkvfYreDqJhZxYAWFPtrfhNpj4w==} + engines: {node: '>=6.9.0'} + + '@babel/parser@7.28.5': + resolution: {integrity: sha512-KKBU1VGYR7ORr3At5HAtUQ+TV3SzRCXmA/8OdDZiLDBIZxVyzXuztPjfLd3BV1PRAQGCMWWSHYhL0F8d5uHBDQ==} + 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.4': resolution: {integrity: sha512-Q/N6JNWvIvPnLDvjlE1OUBLPQHH6l3CltCEsHIujp45zQUSSh8K+gHnaEX45yAT1nyngnINhvWtzN+Nb9D8RAQ==} engines: {node: '>=6.9.0'} + '@babel/template@7.27.2': + resolution: {integrity: sha512-LPDZ85aEJyYSd18/DkjNh4/y1ntkE5KwUHWTiqgRxruuZL2F1yuHligVHLvcHY2vMHXttKFpJn6LwfI7cw7ODw==} + engines: {node: '>=6.9.0'} + + '@babel/traverse@7.28.5': + resolution: {integrity: sha512-TCCj4t55U90khlYkVV/0TfkJkAkUg3jZFA3Neb7unZT8CPok7iiRfaX0F+WnqWqt7OxhOn0uBKXCw4lbL8W0aQ==} + engines: {node: '>=6.9.0'} + + '@babel/types@7.28.5': + resolution: {integrity: sha512-qQ5m48eI/MFLQ5PxQj4PFaprjyCTLI37ElWMmNs0K8Lk3dVeOdNpB3ks8jc7yM5CDmVC73eMVk/trk3fgmrUpA==} + 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.15': + resolution: {integrity: sha512-q0p6zkVq2lJnmzZVPR33doA51G7YOja+FBvRdp5ISIthL0MtFCgYHHhR563z9WFGxcOn0WfjSkPDJ5Qig3H3Sw==} + engines: {node: '>=18'} + + '@csstools/css-tokenizer@3.0.4': + resolution: {integrity: sha512-Vd/9EVDiu6PPJt9yAh6roZP6El1xHrdvIVGjyBsHR0RYwNHgL7FJPyIIW4fANJNG6FtyZfvlRPpFI4ZM/lubvw==} + engines: {node: '>=18'} + '@date-fns/tz@1.2.0': resolution: {integrity: sha512-LBrd7MiJZ9McsOgxqWX7AaxrDjcFVjWH/tIKJd7pnR7McaslGYOP1QmmiBXdJH/H/yLCT+rcQ7FaPBUxRGUtrg==} '@emnapi/runtime@1.7.0': resolution: {integrity: sha512-oAYoQnCYaQZKVS53Fq23ceWMRxq5EhQsE0x0RdQ55jT7wagMu5k+fS39v1fiSLrtrLQlXwVINenqhLMtTrV/1Q==} + '@esbuild/aix-ppc64@0.25.12': + resolution: {integrity: sha512-Hhmwd6CInZ3dwpuGTF8fJG6yoWmsToE+vYgD4nytZVxcu1ulHpUQRAB1UJ8+N1Am3Mz4+xOByoQoSZf4D+CpkA==} + engines: {node: '>=18'} + cpu: [ppc64] + os: [aix] + + '@esbuild/android-arm64@0.25.12': + resolution: {integrity: sha512-6AAmLG7zwD1Z159jCKPvAxZd4y/VTO0VkprYy+3N2FtJ8+BQWFXU+OxARIwA46c5tdD9SsKGZ/1ocqBS/gAKHg==} + engines: {node: '>=18'} + cpu: [arm64] + os: [android] + + '@esbuild/android-arm@0.25.12': + resolution: {integrity: sha512-VJ+sKvNA/GE7Ccacc9Cha7bpS8nyzVv0jdVgwNDaR4gDMC/2TTRc33Ip8qrNYUcpkOHUT5OZ0bUcNNVZQ9RLlg==} + engines: {node: '>=18'} + cpu: [arm] + os: [android] + + '@esbuild/android-x64@0.25.12': + resolution: {integrity: sha512-5jbb+2hhDHx5phYR2By8GTWEzn6I9UqR11Kwf22iKbNpYrsmRB18aX/9ivc5cabcUiAT/wM+YIZ6SG9QO6a8kg==} + engines: {node: '>=18'} + cpu: [x64] + os: [android] + + '@esbuild/darwin-arm64@0.25.12': + resolution: {integrity: sha512-N3zl+lxHCifgIlcMUP5016ESkeQjLj/959RxxNYIthIg+CQHInujFuXeWbWMgnTo4cp5XVHqFPmpyu9J65C1Yg==} + engines: {node: '>=18'} + cpu: [arm64] + os: [darwin] + + '@esbuild/darwin-x64@0.25.12': + resolution: {integrity: sha512-HQ9ka4Kx21qHXwtlTUVbKJOAnmG1ipXhdWTmNXiPzPfWKpXqASVcWdnf2bnL73wgjNrFXAa3yYvBSd9pzfEIpA==} + engines: {node: '>=18'} + cpu: [x64] + os: [darwin] + + '@esbuild/freebsd-arm64@0.25.12': + resolution: {integrity: sha512-gA0Bx759+7Jve03K1S0vkOu5Lg/85dou3EseOGUes8flVOGxbhDDh/iZaoek11Y8mtyKPGF3vP8XhnkDEAmzeg==} + engines: {node: '>=18'} + cpu: [arm64] + os: [freebsd] + + '@esbuild/freebsd-x64@0.25.12': + resolution: {integrity: sha512-TGbO26Yw2xsHzxtbVFGEXBFH0FRAP7gtcPE7P5yP7wGy7cXK2oO7RyOhL5NLiqTlBh47XhmIUXuGciXEqYFfBQ==} + engines: {node: '>=18'} + cpu: [x64] + os: [freebsd] + + '@esbuild/linux-arm64@0.25.12': + resolution: {integrity: sha512-8bwX7a8FghIgrupcxb4aUmYDLp8pX06rGh5HqDT7bB+8Rdells6mHvrFHHW2JAOPZUbnjUpKTLg6ECyzvas2AQ==} + engines: {node: '>=18'} + cpu: [arm64] + os: [linux] + + '@esbuild/linux-arm@0.25.12': + resolution: {integrity: sha512-lPDGyC1JPDou8kGcywY0YILzWlhhnRjdof3UlcoqYmS9El818LLfJJc3PXXgZHrHCAKs/Z2SeZtDJr5MrkxtOw==} + engines: {node: '>=18'} + cpu: [arm] + os: [linux] + + '@esbuild/linux-ia32@0.25.12': + resolution: {integrity: sha512-0y9KrdVnbMM2/vG8KfU0byhUN+EFCny9+8g202gYqSSVMonbsCfLjUO+rCci7pM0WBEtz+oK/PIwHkzxkyharA==} + engines: {node: '>=18'} + cpu: [ia32] + os: [linux] + + '@esbuild/linux-loong64@0.25.12': + resolution: {integrity: sha512-h///Lr5a9rib/v1GGqXVGzjL4TMvVTv+s1DPoxQdz7l/AYv6LDSxdIwzxkrPW438oUXiDtwM10o9PmwS/6Z0Ng==} + engines: {node: '>=18'} + cpu: [loong64] + os: [linux] + + '@esbuild/linux-mips64el@0.25.12': + resolution: {integrity: sha512-iyRrM1Pzy9GFMDLsXn1iHUm18nhKnNMWscjmp4+hpafcZjrr2WbT//d20xaGljXDBYHqRcl8HnxbX6uaA/eGVw==} + engines: {node: '>=18'} + cpu: [mips64el] + os: [linux] + + '@esbuild/linux-ppc64@0.25.12': + resolution: {integrity: sha512-9meM/lRXxMi5PSUqEXRCtVjEZBGwB7P/D4yT8UG/mwIdze2aV4Vo6U5gD3+RsoHXKkHCfSxZKzmDssVlRj1QQA==} + engines: {node: '>=18'} + cpu: [ppc64] + os: [linux] + + '@esbuild/linux-riscv64@0.25.12': + resolution: {integrity: sha512-Zr7KR4hgKUpWAwb1f3o5ygT04MzqVrGEGXGLnj15YQDJErYu/BGg+wmFlIDOdJp0PmB0lLvxFIOXZgFRrdjR0w==} + engines: {node: '>=18'} + cpu: [riscv64] + os: [linux] + + '@esbuild/linux-s390x@0.25.12': + resolution: {integrity: sha512-MsKncOcgTNvdtiISc/jZs/Zf8d0cl/t3gYWX8J9ubBnVOwlk65UIEEvgBORTiljloIWnBzLs4qhzPkJcitIzIg==} + engines: {node: '>=18'} + cpu: [s390x] + os: [linux] + + '@esbuild/linux-x64@0.25.12': + resolution: {integrity: sha512-uqZMTLr/zR/ed4jIGnwSLkaHmPjOjJvnm6TVVitAa08SLS9Z0VM8wIRx7gWbJB5/J54YuIMInDquWyYvQLZkgw==} + engines: {node: '>=18'} + cpu: [x64] + os: [linux] + + '@esbuild/netbsd-arm64@0.25.12': + resolution: {integrity: sha512-xXwcTq4GhRM7J9A8Gv5boanHhRa/Q9KLVmcyXHCTaM4wKfIpWkdXiMog/KsnxzJ0A1+nD+zoecuzqPmCRyBGjg==} + engines: {node: '>=18'} + cpu: [arm64] + os: [netbsd] + + '@esbuild/netbsd-x64@0.25.12': + resolution: {integrity: sha512-Ld5pTlzPy3YwGec4OuHh1aCVCRvOXdH8DgRjfDy/oumVovmuSzWfnSJg+VtakB9Cm0gxNO9BzWkj6mtO1FMXkQ==} + engines: {node: '>=18'} + cpu: [x64] + os: [netbsd] + + '@esbuild/openbsd-arm64@0.25.12': + resolution: {integrity: sha512-fF96T6KsBo/pkQI950FARU9apGNTSlZGsv1jZBAlcLL1MLjLNIWPBkj5NlSz8aAzYKg+eNqknrUJ24QBybeR5A==} + engines: {node: '>=18'} + cpu: [arm64] + os: [openbsd] + + '@esbuild/openbsd-x64@0.25.12': + resolution: {integrity: sha512-MZyXUkZHjQxUvzK7rN8DJ3SRmrVrke8ZyRusHlP+kuwqTcfWLyqMOE3sScPPyeIXN/mDJIfGXvcMqCgYKekoQw==} + engines: {node: '>=18'} + cpu: [x64] + os: [openbsd] + + '@esbuild/openharmony-arm64@0.25.12': + resolution: {integrity: sha512-rm0YWsqUSRrjncSXGA7Zv78Nbnw4XL6/dzr20cyrQf7ZmRcsovpcRBdhD43Nuk3y7XIoW2OxMVvwuRvk9XdASg==} + engines: {node: '>=18'} + cpu: [arm64] + os: [openharmony] + + '@esbuild/sunos-x64@0.25.12': + resolution: {integrity: sha512-3wGSCDyuTHQUzt0nV7bocDy72r2lI33QL3gkDNGkod22EsYl04sMf0qLb8luNKTOmgF/eDEDP5BFNwoBKH441w==} + engines: {node: '>=18'} + cpu: [x64] + os: [sunos] + + '@esbuild/win32-arm64@0.25.12': + resolution: {integrity: sha512-rMmLrur64A7+DKlnSuwqUdRKyd3UE7oPJZmnljqEptesKM8wx9J8gx5u0+9Pq0fQQW8vqeKebwNXdfOyP+8Bsg==} + engines: {node: '>=18'} + cpu: [arm64] + os: [win32] + + '@esbuild/win32-ia32@0.25.12': + resolution: {integrity: sha512-HkqnmmBoCbCwxUKKNPBixiWDGCpQGVsrQfJoVGYLPT41XWF8lHuE5N6WhVia2n4o5QK5M4tYr21827fNhi4byQ==} + engines: {node: '>=18'} + cpu: [ia32] + os: [win32] + + '@esbuild/win32-x64@0.25.12': + resolution: {integrity: sha512-alJC0uCZpTFrSL0CCDjcgleBXPnCrEAhTBILpeAp7M/OFgoqtAetfBzX0xM00MUsVVPpVjlPuMbREqnZCXaTnA==} + engines: {node: '>=18'} + cpu: [x64] + os: [win32] + '@floating-ui/core@1.7.3': resolution: {integrity: sha512-sGnvb5dmrJaKEZ+LDIpguvdX3bDlEllmv4/ClQ9awcmCZrlx5jQyyMWFM5kBI+EyNOCDDiKk8il0zeuX3Zlg/w==} @@ -369,6 +683,9 @@ packages: '@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'} @@ -435,6 +752,9 @@ packages: engines: {node: '>=18'} hasBin: true + '@polka/url@1.0.0-next.29': + resolution: {integrity: sha512-wwQAWhWSuHaag8c4q/KN/vCoeOJYshAIvMQwD4GpSb3OiZklFfvAgmj0VCBBImRpuF/aFgIRzllXlVX93Jevww==} + '@radix-ui/number@1.1.0': resolution: {integrity: sha512-V3gRzhVNU1ldS5XhAPTom1fOIo4ccrjjJgmE+LI2h/WaFpHmx0MQApT+KZHnx8abG6Avtfcz4WoEciMnpFT3HQ==} @@ -1329,6 +1649,122 @@ packages: '@radix-ui/rect@1.1.1': resolution: {integrity: sha512-HPwpGIzkl28mWyZqG52jiqDJ12waP11Pa1lGoiyUkIEuMLBP0oeK/C89esbXrxsky5we7dfd8U58nm0SgAWpVw==} + '@rolldown/pluginutils@1.0.0-beta.43': + resolution: {integrity: sha512-5Uxg7fQUCmfhax7FJke2+8B6cqgeUJUD9o2uXIKXhD+mG0mL6NObmVoi9wXEU1tY89mZKgAYA6fTbftx3q2ZPQ==} + + '@rollup/rollup-android-arm-eabi@4.52.5': + resolution: {integrity: sha512-8c1vW4ocv3UOMp9K+gToY5zL2XiiVw3k7f1ksf4yO1FlDFQ1C2u72iACFnSOceJFsWskc2WZNqeRhFRPzv+wtQ==} + cpu: [arm] + os: [android] + + '@rollup/rollup-android-arm64@4.52.5': + resolution: {integrity: sha512-mQGfsIEFcu21mvqkEKKu2dYmtuSZOBMmAl5CFlPGLY94Vlcm+zWApK7F/eocsNzp8tKmbeBP8yXyAbx0XHsFNA==} + cpu: [arm64] + os: [android] + + '@rollup/rollup-darwin-arm64@4.52.5': + resolution: {integrity: sha512-takF3CR71mCAGA+v794QUZ0b6ZSrgJkArC+gUiG6LB6TQty9T0Mqh3m2ImRBOxS2IeYBo4lKWIieSvnEk2OQWA==} + cpu: [arm64] + os: [darwin] + + '@rollup/rollup-darwin-x64@4.52.5': + resolution: {integrity: sha512-W901Pla8Ya95WpxDn//VF9K9u2JbocwV/v75TE0YIHNTbhqUTv9w4VuQ9MaWlNOkkEfFwkdNhXgcLqPSmHy0fA==} + cpu: [x64] + os: [darwin] + + '@rollup/rollup-freebsd-arm64@4.52.5': + resolution: {integrity: sha512-QofO7i7JycsYOWxe0GFqhLmF6l1TqBswJMvICnRUjqCx8b47MTo46W8AoeQwiokAx3zVryVnxtBMcGcnX12LvA==} + cpu: [arm64] + os: [freebsd] + + '@rollup/rollup-freebsd-x64@4.52.5': + resolution: {integrity: sha512-jr21b/99ew8ujZubPo9skbrItHEIE50WdV86cdSoRkKtmWa+DDr6fu2c/xyRT0F/WazZpam6kk7IHBerSL7LDQ==} + cpu: [x64] + os: [freebsd] + + '@rollup/rollup-linux-arm-gnueabihf@4.52.5': + resolution: {integrity: sha512-PsNAbcyv9CcecAUagQefwX8fQn9LQ4nZkpDboBOttmyffnInRy8R8dSg6hxxl2Re5QhHBf6FYIDhIj5v982ATQ==} + cpu: [arm] + os: [linux] + + '@rollup/rollup-linux-arm-musleabihf@4.52.5': + resolution: {integrity: sha512-Fw4tysRutyQc/wwkmcyoqFtJhh0u31K+Q6jYjeicsGJJ7bbEq8LwPWV/w0cnzOqR2m694/Af6hpFayLJZkG2VQ==} + cpu: [arm] + os: [linux] + + '@rollup/rollup-linux-arm64-gnu@4.52.5': + resolution: {integrity: sha512-a+3wVnAYdQClOTlyapKmyI6BLPAFYs0JM8HRpgYZQO02rMR09ZcV9LbQB+NL6sljzG38869YqThrRnfPMCDtZg==} + cpu: [arm64] + os: [linux] + + '@rollup/rollup-linux-arm64-musl@4.52.5': + resolution: {integrity: sha512-AvttBOMwO9Pcuuf7m9PkC1PUIKsfaAJ4AYhy944qeTJgQOqJYJ9oVl2nYgY7Rk0mkbsuOpCAYSs6wLYB2Xiw0Q==} + cpu: [arm64] + os: [linux] + + '@rollup/rollup-linux-loong64-gnu@4.52.5': + resolution: {integrity: sha512-DkDk8pmXQV2wVrF6oq5tONK6UHLz/XcEVow4JTTerdeV1uqPeHxwcg7aFsfnSm9L+OO8WJsWotKM2JJPMWrQtA==} + cpu: [loong64] + os: [linux] + + '@rollup/rollup-linux-ppc64-gnu@4.52.5': + resolution: {integrity: sha512-W/b9ZN/U9+hPQVvlGwjzi+Wy4xdoH2I8EjaCkMvzpI7wJUs8sWJ03Rq96jRnHkSrcHTpQe8h5Tg3ZzUPGauvAw==} + cpu: [ppc64] + os: [linux] + + '@rollup/rollup-linux-riscv64-gnu@4.52.5': + resolution: {integrity: sha512-sjQLr9BW7R/ZiXnQiWPkErNfLMkkWIoCz7YMn27HldKsADEKa5WYdobaa1hmN6slu9oWQbB6/jFpJ+P2IkVrmw==} + cpu: [riscv64] + os: [linux] + + '@rollup/rollup-linux-riscv64-musl@4.52.5': + resolution: {integrity: sha512-hq3jU/kGyjXWTvAh2awn8oHroCbrPm8JqM7RUpKjalIRWWXE01CQOf/tUNWNHjmbMHg/hmNCwc/Pz3k1T/j/Lg==} + cpu: [riscv64] + os: [linux] + + '@rollup/rollup-linux-s390x-gnu@4.52.5': + resolution: {integrity: sha512-gn8kHOrku8D4NGHMK1Y7NA7INQTRdVOntt1OCYypZPRt6skGbddska44K8iocdpxHTMMNui5oH4elPH4QOLrFQ==} + cpu: [s390x] + os: [linux] + + '@rollup/rollup-linux-x64-gnu@4.52.5': + resolution: {integrity: sha512-hXGLYpdhiNElzN770+H2nlx+jRog8TyynpTVzdlc6bndktjKWyZyiCsuDAlpd+j+W+WNqfcyAWz9HxxIGfZm1Q==} + cpu: [x64] + os: [linux] + + '@rollup/rollup-linux-x64-musl@4.52.5': + resolution: {integrity: sha512-arCGIcuNKjBoKAXD+y7XomR9gY6Mw7HnFBv5Rw7wQRvwYLR7gBAgV7Mb2QTyjXfTveBNFAtPt46/36vV9STLNg==} + cpu: [x64] + os: [linux] + + '@rollup/rollup-openharmony-arm64@4.52.5': + resolution: {integrity: sha512-QoFqB6+/9Rly/RiPjaomPLmR/13cgkIGfA40LHly9zcH1S0bN2HVFYk3a1eAyHQyjs3ZJYlXvIGtcCs5tko9Cw==} + cpu: [arm64] + os: [openharmony] + + '@rollup/rollup-win32-arm64-msvc@4.52.5': + resolution: {integrity: sha512-w0cDWVR6MlTstla1cIfOGyl8+qb93FlAVutcor14Gf5Md5ap5ySfQ7R9S/NjNaMLSFdUnKGEasmVnu3lCMqB7w==} + cpu: [arm64] + os: [win32] + + '@rollup/rollup-win32-ia32-msvc@4.52.5': + resolution: {integrity: sha512-Aufdpzp7DpOTULJCuvzqcItSGDH73pF3ko/f+ckJhxQyHtp67rHw3HMNxoIdDMUITJESNE6a8uh4Lo4SLouOUg==} + cpu: [ia32] + os: [win32] + + '@rollup/rollup-win32-x64-gnu@4.52.5': + resolution: {integrity: sha512-UGBUGPFp1vkj6p8wCRraqNhqwX/4kNQPS57BCFc8wYh0g94iVIW33wJtQAx3G7vrjjNtRaxiMUylM0ktp/TRSQ==} + cpu: [x64] + os: [win32] + + '@rollup/rollup-win32-x64-msvc@4.52.5': + resolution: {integrity: sha512-TAcgQh2sSkykPRWLrdyy2AiceMckNf5loITqXxFI5VuQjS5tSuw3WlwdN8qv8vzjLAUTvYaH/mVjSFpbkFbpTg==} + cpu: [x64] + os: [win32] + + '@standard-schema/spec@1.0.0': + resolution: {integrity: sha512-m2bOd0f2RT9k8QJx1JN85cZYyH1RqFBdlwtkSlf4tBDYLCiiZnv1fIIwacK6cqwXavOydf0NPToMQgpKq+dVlA==} + '@supabase/auth-js@2.78.0': resolution: {integrity: sha512-cXDtu1U0LeZj/xfnFoV7yCze37TcbNo8FCxy1FpqhMbB9u9QxxDSW6pA5gm/07Ei7m260Lof4CZx67Cu6DPeig==} @@ -1447,9 +1883,56 @@ packages: '@tailwindcss/postcss@4.1.9': resolution: {integrity: sha512-v3DKzHibZO8ioVDmuVHCW1PR0XSM7nS40EjZFJEA1xPuvTuQPaR5flE1LyikU3hu2u1KNWBtEaSe8qsQjX3tyg==} + '@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.0': + resolution: {integrity: sha512-kFSyxiEDwv1WLl2fgsq6pPBbw5aWKrsY2/noi1Id0TK0UParSF62oFQFGHXIyaG4pp2tEub/Zlel+fjjZILDsw==} + 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 + + '@testing-library/user-event@14.6.1': + resolution: {integrity: sha512-vq7fv0rnt+QTXgPxr5Hjc210p6YKq2kmdziLgnsZGgLJ9e6VAShx1pACLuRjd/AS/sr7phAR58OIIpf0LlmQNw==} + engines: {node: '>=12', npm: '>=6'} + peerDependencies: + '@testing-library/dom': '>=7.21.4' + '@testmail.app/graphql-request@1.8.4': resolution: {integrity: sha512-tHTXfoSLNlZJIKLI/6lpNYriIqigg8Ga8JRatY3978YL4hEvt5DswcRPFAd2O3v/ulv5OfAIzWF66Z5XteNfFw==} + '@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/d3-array@3.2.2': resolution: {integrity: sha512-hOLWVbm7uRza0BYXpIIW5pxfrKe0W+D5lrFiAEYR+pb6w3N2SwSMaJbXdUfSEv+dT4MfHBLtn5js0LAWaO6otw==} @@ -1477,6 +1960,12 @@ packages: '@types/d3-timer@3.0.2': resolution: {integrity: sha512-Ps3T8E8dZDam6fUyNiMkekK3XUsaUEik+idO9/YjPtfj2qruF8tFBXS7XhtE4iIXBLxhmLjP3SXpLhVf21I9Lw==} + '@types/deep-eql@4.0.2': + resolution: {integrity: sha512-c9h9dVVMigMPc4bwTvC5dxqtqJZwQPePsWjPlpSOnojbor6pGqdk541lfA7AqFQr5pB1BRdq0juY9db81BwyFw==} + + '@types/estree@1.0.8': + resolution: {integrity: sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==} + '@types/node@22.0.0': resolution: {integrity: sha512-VT7KSYudcPOzP5Q0wfbowyNLaVR8QWUdw+088uFWwfvpY6uCWaXpqV6ieLAu9WBcnTa7H4Z5RLK8I5t2FuOcqw==} @@ -1518,21 +2007,99 @@ packages: vue-router: optional: true - aria-hidden@1.2.6: - resolution: {integrity: sha512-ik3ZgC9dY/lYVVM++OISsaYDeg1tb0VtP5uL3ouh1koGOaUMDPpbFIei4JkFimWUFPn90sbMNMXQAIVOlnYKJA==} - engines: {node: '>=10'} + '@vitejs/plugin-react@5.1.0': + resolution: {integrity: sha512-4LuWrg7EKWgQaMJfnN+wcmbAW+VSsCmqGohftWjuct47bv8uE4n/nPpq4XjJPsxgq00GGG5J8dvBczp8uxScew==} + engines: {node: ^20.19.0 || >=22.12.0} + peerDependencies: + vite: ^4.2.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 - autoprefixer@10.4.20: - resolution: {integrity: sha512-XY25y5xSv/wEoqzDyXXME4AFfkZI0P23z6Fs3YgymDnKJkCGOnkL0iTxCa85UTqaSgfcqyf3UA6+c7wUvx/16g==} - engines: {node: ^10 || ^12 || >=14} - hasBin: true + '@vitest/coverage-v8@4.0.7': + resolution: {integrity: sha512-MXc+kEA5EUwMMGmNt1S6CIOEl/iCmAhGZQq1QgMNC3/QpYSOxkysEi6pxWhkqJ7YT/RduoVEV5rxFxHG18V3LA==} peerDependencies: - postcss: ^8.1.0 + '@vitest/browser': 4.0.7 + vitest: 4.0.7 + peerDependenciesMeta: + '@vitest/browser': + optional: true + + '@vitest/expect@4.0.7': + resolution: {integrity: sha512-jGRG6HghnJDjljdjYIoVzX17S6uCVCBRFnsgdLGJ6CaxfPh8kzUKe/2n533y4O/aeZ/sIr7q7GbuEbeGDsWv4Q==} + + '@vitest/mocker@4.0.7': + resolution: {integrity: sha512-OsDwLS7WnpuNslOV6bJkXVYVV/6RSc4eeVxV7h9wxQPNxnjRvTTrIikfwCbMyl8XJmW6oOccBj2Q07YwZtQcCw==} + 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.7': + resolution: {integrity: sha512-YY//yxqTmk29+/pK+Wi1UB4DUH3lSVgIm+M10rAJ74pOSMgT7rydMSc+vFuq9LjZLhFvVEXir8EcqMke3SVM6Q==} + + '@vitest/runner@4.0.7': + resolution: {integrity: sha512-orU1lsu4PxLEcDWfjVCNGIedOSF/YtZ+XMrd1PZb90E68khWCNzD8y1dtxtgd0hyBIQk8XggteKN/38VQLvzuw==} + + '@vitest/snapshot@4.0.7': + resolution: {integrity: sha512-xJL+Nkw0OjaUXXQf13B8iKK5pI9QVtN9uOtzNHYuG/o/B7fIEg0DQ+xOe0/RcqwDEI15rud1k7y5xznBKGUXAA==} + + '@vitest/spy@4.0.7': + resolution: {integrity: sha512-FW4X8hzIEn4z+HublB4hBF/FhCVaXfIHm8sUfvlznrcy1MQG7VooBgZPMtVCGZtHi0yl3KESaXTqsKh16d8cFg==} + + '@vitest/ui@4.0.7': + resolution: {integrity: sha512-aIFPci9xoTmVkxpqsSKcRG/Hn0lTy421jsCehHydYeIMd+getn0Pue0JqY5cW8yZglZjMeX0YfIy5wDtQDHEcA==} + peerDependencies: + vitest: 4.0.7 + + '@vitest/utils@4.0.7': + resolution: {integrity: sha512-HNrg9CM/Z4ZWB6RuExhuC6FPmLipiShKVMnT9JlQvfhwR47JatWLChA6mtZqVHqypE6p/z6ofcjbyWpM7YLxPQ==} + + agent-base@7.1.4: + resolution: {integrity: sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ==} + engines: {node: '>= 14'} + + 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'} + + aria-hidden@1.2.6: + resolution: {integrity: sha512-ik3ZgC9dY/lYVVM++OISsaYDeg1tb0VtP5uL3ouh1koGOaUMDPpbFIei4JkFimWUFPn90sbMNMXQAIVOlnYKJA==} + engines: {node: '>=10'} + + 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.8: + resolution: {integrity: sha512-szgSZqUxI5T8mLKvS7WTjF9is+MVbOeLADU73IseOcrqhxr/VAvy6wfoVE39KnKzA7JRhjF5eUagNlHwvZPlKQ==} + + autoprefixer@10.4.20: + resolution: {integrity: sha512-XY25y5xSv/wEoqzDyXXME4AFfkZI0P23z6Fs3YgymDnKJkCGOnkL0iTxCa85UTqaSgfcqyf3UA6+c7wUvx/16g==} + engines: {node: ^10 || ^12 || >=14} + hasBin: true + peerDependencies: + postcss: ^8.1.0 baseline-browser-mapping@2.8.23: resolution: {integrity: sha512-616V5YX4bepJFzNyOfce5Fa8fDJMfoxzOIzDCZwaGL8MKVpFrXqfNUoIpRn9YMI5pXf/VKgzjB4htFMsFKKdiQ==} hasBin: true + bidi-js@1.0.3: + resolution: {integrity: sha512-RKshQI1R3YQ+n9YJz2QQ147P66ELpa1FQEg20Dk8oW9t2KgLbpDLLp9aGZ7y8WHSshDknG0bknqGw5/tyCs5tw==} + browserslist@4.27.0: resolution: {integrity: sha512-AXVQwdhot1eqLihwasPElhX2tAZiBjWdJ9i/Zcj2S6QYIjkx62OKSfnobkriB81C3l4w0rVy3Nt4jaTBltYEpw==} engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7} @@ -1541,6 +2108,10 @@ packages: caniuse-lite@1.0.30001753: resolution: {integrity: sha512-Bj5H35MD/ebaOV4iDLqPEtiliTN29qkGtEHCwawWn4cYm+bPJM2NsaP30vtZcnERClMzp52J4+aw2UNbK4o+zw==} + chai@6.2.0: + resolution: {integrity: sha512-aUTnJc/JipRzJrNADXVvpVqi6CO0dn3nx4EVPxijri+fj3LUUDyZQOgVeW54Ob3Y1Xh9Iz8f+CgaCl8v0mn9bA==} + engines: {node: '>=18'} + chownr@3.0.0: resolution: {integrity: sha512-+IxzY9BZOQd/XuYPRmrvEVjF/nqj5kgT4kEq7VofrDoM1MxoRjEWkrCC3EtLi59TVawxTAn+orJwFQcrqEN1+g==} engines: {node: '>=18'} @@ -1561,6 +2132,9 @@ packages: react: ^18 || ^19 || ^19.0.0-rc react-dom: ^18 || ^19 || ^19.0.0-rc + convert-source-map@2.0.0: + resolution: {integrity: sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==} + cookie@1.0.2: resolution: {integrity: sha512-9Kr/j4O16ISv8zBBhJoi4bXOYNTkFLOqSL3UDB0njXxCXNezjeyVrJyGOWtgfs/q2km1gwBcfH8q1yEGoMYunA==} engines: {node: '>=18'} @@ -1568,6 +2142,17 @@ packages: cross-fetch@3.2.0: resolution: {integrity: sha512-Q+xVJLoGOeIMXZmbUK4HYk+69cQH6LudR0Vu/pRm2YlU/hDV9CiS0gKUMaWY5f2NeUH9C1nV3bsTlCo0FsTV1Q==} + 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.2: + resolution: {integrity: sha512-zDMqXh8Vs1CdRYZQ2M633m/SFgcjlu8RB8b/1h82i+6vpArF507NSYIWJHGlJaTWoS+imcnctmEz43txhbVkOw==} + engines: {node: '>=20'} + csstype@3.1.3: resolution: {integrity: sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==} @@ -1615,15 +2200,35 @@ packages: resolution: {integrity: sha512-ndfJ/JxxMd3nw31uyKoY2naivF+r29V+Lc0svZxe1JvvIRmi8hUsrMvdOwgS1o6uBHmiz91geQ0ylPP0aj1VUA==} engines: {node: '>=12'} + data-urls@6.0.0: + resolution: {integrity: sha512-BnBS08aLUM+DKamupXs3w2tJJoqU+AkaE/+6vQxi/G/DPmIZFJJp9Dkb1kM03AZx8ADehDUZgsNxju3mPXZYIA==} + engines: {node: '>=20'} + date-fns-jalali@4.1.0-0: resolution: {integrity: sha512-hTIP/z+t+qKwBDcmmsnmjWTduxCg+5KfdqWQvb2X/8C9+knYY6epN/pfxdDuyVlSVeFz0sM5eEfwIUQ70U4ckg==} date-fns@4.1.0: resolution: {integrity: sha512-Ukq0owbQXxa/U3EGtsdVBkR1w7KOQ5gIBqdH2hkvknzZPYvBxb/aa6E8L7tmjFtkwZBu3UXBbjIgPo/Ez4xaNg==} + debug@4.4.3: + resolution: {integrity: sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==} + engines: {node: '>=6.0'} + peerDependencies: + supports-color: '*' + peerDependenciesMeta: + supports-color: + optional: true + decimal.js-light@2.5.1: resolution: {integrity: sha512-qIMFpTMZmny+MMIitAB6D7iVPEorVw6YQRWkvarTkT4tBeSLLiHzcwj6q0MmYSFCiVpiqPJTJEYIrpcPzVEIvg==} + decimal.js@10.6.0: + resolution: {integrity: sha512-YpgQiITW3JXGntzdUmyUR1V812Hn8T1YVXhCu+wO3OpS4eU9l4YdD3qjyiKdV6mvV29zapkMeD390UVEf2lkUg==} + + dequal@2.0.3: + resolution: {integrity: sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==} + engines: {node: '>=6'} + detect-libc@2.1.2: resolution: {integrity: sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==} engines: {node: '>=8'} @@ -1631,6 +2236,12 @@ packages: detect-node-es@1.1.0: resolution: {integrity: sha512-ypdmJU/TbBby2Dxibuv7ZLW3Bs1QEmM7nHjEANfohJLvE0XVujisn1qPJcZxg+qDucsr+bP6fLD1rPS3AhJ7EQ==} + dom-accessibility-api@0.5.16: + resolution: {integrity: sha512-X7BJ2yElsnOJ30pZF4uIIDfBEVgF4XEBxL9Bxhy6dnrm5hkzqmsWHGTiHqRiITNhMyFLyAiWndIJP7Z1NTteDg==} + + dom-accessibility-api@0.6.3: + resolution: {integrity: sha512-7ZgogeTnjuHbo+ct10G9Ffp0mif17idi0IyWNVA/wcwcm7NPOD/WEHVP3n7n3MhXqxoIYm8d6MuZohYWIZ4T3w==} + dom-helpers@5.2.1: resolution: {integrity: sha512-nRCa7CK3VTrM2NmGkIy4cbK7IZlgBE/PYMn55rrXefr5xXDP0LdtfPnblFDoVdcAfslJ7or6iqAUnx0CCGIWQA==} @@ -1654,23 +2265,57 @@ packages: resolution: {integrity: sha512-d4lC8xfavMeBjzGr2vECC3fsGXziXZQyJxD868h2M/mBI3PwAuODxAkLkq5HYuvrPYcUtiLzsTo8U3PgX3Ocww==} engines: {node: '>=10.13.0'} + entities@6.0.1: + resolution: {integrity: sha512-aN97NXWF6AWBTahfVOIrB/NShkzi5H7F9r1s9mD3cDj4Ko5f2qhhVoYMibXF7GlLveb/D2ioWay8lxI97Ven3g==} + engines: {node: '>=0.12'} + + es-module-lexer@1.7.0: + resolution: {integrity: sha512-jEQoCwk8hyb2AZziIOLhDqpm5+2ww5uIE6lkO/6jcOCusfk6LhMHpXXfBLXTZ7Ydyt0j4VoUQv6uGNYbdW+kBA==} + es6-promise@4.2.8: resolution: {integrity: sha512-HJDGx5daxeIvxdBxvG2cb9g4tEvwIk3i8+nhX0yGrYmZUzbkdg8QbDevheDB8gd0//uPj4c1EQua8Q+MViT0/w==} + 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@3.0.3: + resolution: {integrity: sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==} + eventemitter3@4.0.7: resolution: {integrity: sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==} + expect-type@1.2.2: + resolution: {integrity: sha512-JhFGDVJ7tmDJItKhYgJCGLOWjuK9vPxiXoUFLwLDc99NlmklilbiQJwoctZtt13+xMw91MCk/REan6MWHqDjyA==} + engines: {node: '>=12.0.0'} + fast-equals@5.3.2: resolution: {integrity: sha512-6rxyATwPCkaFIL3JLqw8qXqMpIZ942pTX/tbQFkRsDGblS8tNGtlUauA/+mt6RUfqn/4MoEr+WDkYoIQbibWuQ==} engines: {node: '>=6.0.0'} + fdir@6.5.0: + resolution: {integrity: sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==} + engines: {node: '>=12.0.0'} + peerDependencies: + picomatch: ^3 || ^4 + peerDependenciesMeta: + picomatch: + optional: true + fetch-retry@3.2.3: resolution: {integrity: sha512-baMBEv4uZ1X1cUZAvnM+C9XI7tl4CgHgJE0KBHo3JzuXO7atOeWD5HSkDA2oLYpbzLTZNslFckLkIn6T96hlew==} + fflate@0.8.2: + resolution: {integrity: sha512-cPJU47OaAoCbg0pBvzsgpTPhmhqI5eJjh/JIu8tPj5q+T7iLvW/JAYUqmE7KOB4R1ZyEhzBaIQpQpardBF5z8A==} + + flatted@3.3.3: + resolution: {integrity: sha512-GX+ysw4PBCz0PzosHDepZGANEuFCMLrnRTiEy9McGjmkCQYwRq4A/X786G/fjM/+OjsWSU1ZrY5qyARZmO/uwg==} + fraction.js@4.3.7: resolution: {integrity: sha512-ZsDfxO51wGAXREY55a7la9LScWpwv9RxIrYABrlvOFBlH/ShPnrtsXeuUIfXKKOVicNxQ+o8JTbJvjS4M89yew==} @@ -1679,6 +2324,15 @@ packages: engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} os: [darwin] + fsevents@2.3.3: + resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==} + engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} + os: [darwin] + + gensync@1.0.0-beta.2: + resolution: {integrity: sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==} + engines: {node: '>=6.9.0'} + get-nonce@1.0.1: resolution: {integrity: sha512-FJhYRoDaiatfEkUK8HKlicmu/3SGFD51q3itKDGoSTysQJBnfOcxU5GxnhE1E6soB76MbT0MBtnKJuXyAx+96Q==} engines: {node: '>=6'} @@ -1686,6 +2340,33 @@ packages: graceful-fs@4.2.11: resolution: {integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==} + has-flag@4.0.0: + resolution: {integrity: sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==} + engines: {node: '>=8'} + + html-encoding-sniffer@4.0.0: + resolution: {integrity: sha512-Y22oTqIU4uuPgEemfz7NDJz6OeKf12Lsu+QC+s3BVpda64lTiMYCyGwg5ki4vFxkMwQdeZDl2adZoqUgdFuTgQ==} + engines: {node: '>=18'} + + 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'} + + iconv-lite@0.6.3: + resolution: {integrity: sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==} + engines: {node: '>=0.10.0'} + + indent-string@4.0.0: + resolution: {integrity: sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==} + engines: {node: '>=8'} + input-otp@1.4.1: resolution: {integrity: sha512-+yvpmKYKHi9jIGngxagY9oWiiblPB7+nEO75F2l2o4vs+6vpPZZmUl4tBNYuTCvQjhvEIbdNeJu70bhfYP2nbw==} peerDependencies: @@ -1696,6 +2377,25 @@ packages: resolution: {integrity: sha512-5Hh7Y1wQbvY5ooGgPbDaL5iYLAPzMTUrjMulskHLH6wnv/A+1q5rgEaiuqEjB+oxGXIVZs1FF+R/KPN3ZSQYYg==} engines: {node: '>=12'} + 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-lib-source-maps@5.0.6: + resolution: {integrity: sha512-yg2d+Em4KizZC5niWhQaIomgf5WlL4vOOjZ5xGCmF8SnPE/mDWWXgvRExdcpCgh9lLRRa1/fSYp2ymmbJ1pI+A==} + engines: {node: '>=10'} + + istanbul-reports@3.2.0: + resolution: {integrity: sha512-HGYWWS/ehqTV3xN10i23tkPkpH46MLCIMFNCaaKNavAXTF1RkqxawEPtnjnGZ6XKSInBKkiOA5BKS+aZiY3AvA==} + engines: {node: '>=8'} + jiti@2.6.1: resolution: {integrity: sha512-ekilCSN1jwRvIbgeg/57YFh8qQDNbwDb9xT/qu2DAHbFFZUicIl4ygVaAvzveMhMVr3LnpSKTNnwt8PoOfmKhQ==} hasBin: true @@ -1703,6 +2403,28 @@ packages: js-tokens@4.0.0: resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==} + js-tokens@9.0.1: + resolution: {integrity: sha512-mxa9E9ITFOt0ban3j6L5MpjwegGz6lBQmM1IJkWeBZGcMxto50+eWdjC/52xDbS2vy0k7vIMK0Fe2wfL9OQSpQ==} + + jsdom@27.1.0: + resolution: {integrity: sha512-Pcfm3eZ+eO4JdZCXthW9tCDT3nF4K+9dmeZ+5X39n+Kqz0DDIABRP5CAEOHRFZk8RGuC2efksTJxrjp8EXCunQ==} + 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 + + json5@2.2.3: + resolution: {integrity: sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==} + engines: {node: '>=6'} + hasBin: true + lightningcss-darwin-arm64@1.30.1: resolution: {integrity: sha512-c8JK7hyE65X1MHMN+Viq9n11RRC7hgin3HhYKhrMyaXflk5GVplZ60IxyoVtzILeKr+xAJwg6zK6sjTBJ0FKYQ==} engines: {node: '>= 12.0.0'} @@ -1774,14 +2496,39 @@ packages: resolution: {integrity: sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==} hasBin: true + lru-cache@11.2.2: + resolution: {integrity: sha512-F9ODfyqML2coTIsQpSkRHnLSZMtkU8Q+mSfcaIyKwy58u+8k5nvAYeiNhsyMARvzNcXJ9QfWVrcPsC9e9rAxtg==} + engines: {node: 20 || >=22} + + lru-cache@5.1.1: + resolution: {integrity: sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==} + lucide-react@0.454.0: resolution: {integrity: sha512-hw7zMDwykCLnEzgncEEjHeA6+45aeEzRYuKHuyRSOPkhko+J3ySGjGIzu+mmMfDFG1vazHepMaYFYHbTFAZAAQ==} peerDependencies: react: ^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0-rc + 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.3.5: + resolution: {integrity: sha512-L0WhttDl+2BOsybvEOLK7fW3UA0OQ0IQ2d6Zl2x/a6vVRs3bAY0ECOSHHeL5jD+SbOpOCUEi0y1DgHEn9Qn1AQ==} + + 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'} + minipass@7.1.2: resolution: {integrity: sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==} engines: {node: '>=16 || 14 >=14.17'} @@ -1790,6 +2537,13 @@ packages: resolution: {integrity: sha512-KZxYo1BUkWD2TVFLr0MQoM8vUUigWD3LlD83a/75BqC+4qE0Hb1Vo5v1FgcfaNXvfXzr+5EhQ6ing/CaBijTlw==} engines: {node: '>= 18'} + mrmime@2.0.1: + resolution: {integrity: sha512-Y3wQdFg2Va6etvQ5I82yUhGdsKrcYox6p7FfL1LbK2J4V01F9TGlepTIhnK24t7koZibmg82KGglhA1XK5IsLQ==} + engines: {node: '>=10'} + + ms@2.1.3: + resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==} + nanoid@3.3.11: resolution: {integrity: sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==} engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} @@ -1842,9 +2596,19 @@ packages: resolution: {integrity: sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==} engines: {node: '>=0.10.0'} + parse5@8.0.0: + resolution: {integrity: sha512-9m4m5GSgXjL4AjumKzq1Fgfp3Z8rsvjRNbnkVwfu2ImRqE5D0LnY2QfDen18FSY9C573YU5XxSapdHZTZ2WolA==} + + pathe@2.0.3: + resolution: {integrity: sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==} + picocolors@1.1.1: resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==} + picomatch@4.0.3: + resolution: {integrity: sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==} + engines: {node: '>=12'} + playwright-core@1.56.1: resolution: {integrity: sha512-hutraynyn31F+Bifme+Ps9Vq59hKuUCz7H1kDOcBs+2oGguKkWTU50bBWrtz34OUWmIwpBTWDxaRPXrIXkgvmQ==} engines: {node: '>=18'} @@ -1866,9 +2630,21 @@ packages: resolution: {integrity: sha512-27VKOqrYfPncKA2NrFOVhP5MGAfHKLYn/Q0mz9cNQyRAKYi3VNHwYU2qKKqPCqgBmeeJ0uAFB56NumXZ5ZReXg==} engines: {node: ^10 || ^12 || >=14} + postcss@8.5.6: + resolution: {integrity: sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==} + engines: {node: ^10 || ^12 || >=14} + + 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} + prop-types@15.8.1: resolution: {integrity: sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==} + punycode@2.3.1: + resolution: {integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==} + engines: {node: '>=6'} + react-day-picker@9.8.0: resolution: {integrity: sha512-E0yhhg7R+pdgbl/2toTb0xBhsEAtmAx1l7qjIWYfcxOy8w4rTSVfbtBoSzVVhPwKP/5E9iL38LivzoE3AQDhCQ==} engines: {node: '>=18'} @@ -1889,9 +2665,16 @@ packages: react-is@16.13.1: resolution: {integrity: sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==} + react-is@17.0.2: + resolution: {integrity: sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==} + react-is@18.3.1: resolution: {integrity: sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==} + react-refresh@0.18.0: + resolution: {integrity: sha512-QgT5//D3jfjJb6Gsjxv0Slpj23ip+HtOpnNgnb2S5zU3CB26G/IDPGoy4RJB42wzFE46DRsstbW6tKHoKbhAxw==} + engines: {node: '>=0.10.0'} + react-remove-scroll-bar@2.3.8: resolution: {integrity: sha512-9r+yi9+mgU33AKcj6IbT9oRCO78WriSj6t/cF8DWBZJ9aOGPOTEDvdUDz1FwKim7QXWwmHqtdHnRJfhAxEG46Q==} engines: {node: '>=10'} @@ -1954,9 +2737,33 @@ packages: react: ^16.0.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 react-dom: ^16.0.0 || ^17.0.0 || ^18.0.0 || ^19.0.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'} + + rollup@4.52.5: + resolution: {integrity: sha512-3GuObel8h7Kqdjt0gxkEzaifHTqLVW56Y/bjN7PSQtkKr0w3V/QYSdt6QWYtd7A1xUtYQigtdUfgj1RvWVtorw==} + engines: {node: '>=18.0.0', npm: '>=8.0.0'} + hasBin: true + + safer-buffer@2.1.2: + resolution: {integrity: sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==} + + 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.7.3: resolution: {integrity: sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==} engines: {node: '>=10'} @@ -1966,6 +2773,13 @@ packages: resolution: {integrity: sha512-FUH39xp3SBPnxWvd5iib1X8XY7J0K0X7d93sie9CJg2PO8/7gmg89Nve6OjItK53/MlAushNNxteBYfM6DEuoA==} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + siginfo@2.0.0: + resolution: {integrity: sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==} + + sirv@3.0.2: + resolution: {integrity: sha512-2wcC/oGxHis/BoHkkPwldgiPSYcpZK3JU28WoMVv55yHJgcZ8rlXvuG9iZggz+sU1d4bRgIGASwyWqjxu3FM0g==} + engines: {node: '>=18'} + sonner@1.7.4: resolution: {integrity: sha512-DIS8z4PfJRbIyfVFDVnK9rO3eYDtse4Omcm6bt0oEr5/jtLgysmjuBl1frJ9E/EQZrFmKx2A8m/s5s9CRXIzhw==} peerDependencies: @@ -1976,6 +2790,16 @@ packages: resolution: {integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==} engines: {node: '>=0.10.0'} + stackback@0.0.2: + resolution: {integrity: sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==} + + std-env@3.10.0: + resolution: {integrity: sha512-5GS12FdOZNliM5mAOxFRg7Ir0pWz8MdpYm6AY6VPkGpbA7ZzmbzNcBJQ0GPvvyWgcY7QAhCgf9Uy89I03faLkg==} + + strip-indent@3.0.0: + resolution: {integrity: sha512-laJTa3Jb+VQpaC6DseHhF7dXVqHTfJPCRDaEbid/drOhgitgYku/letMUqOXFoWV0zIIUbjpdH2t+tYj4bQMRQ==} + engines: {node: '>=8'} + styled-jsx@5.1.6: resolution: {integrity: sha512-qSVyDTeMotdvQYoHWLNGwRFJHC+i+ZvdBRYosOFgC+Wg1vx4frN2/RG/NA7SYqqvKNLf39P2LSRA2pu6n0XYZA==} engines: {node: '>= 12.0.0'} @@ -1989,6 +2813,13 @@ packages: babel-plugin-macros: optional: true + supports-color@7.2.0: + resolution: {integrity: sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==} + engines: {node: '>=8'} + + symbol-tree@3.2.4: + resolution: {integrity: sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==} + tailwind-merge@2.5.5: resolution: {integrity: sha512-0LXunzzAZzo0tEPxV3I297ffKZPlKDrjj7NXphC8V5ak9yHC5zRmxnOe2m/Rd/7ivsOMJe3JZ2JVocoDdQTRBA==} @@ -2011,9 +2842,42 @@ packages: tiny-invariant@1.3.3: resolution: {integrity: sha512-+FbBPE1o9QAYvviau/qC5SE3caw21q3xkvWKBtja5vgqOWIHHJ3ioaq1VPfn/Szqctz2bU/oYeKd9/z5BL+PVg==} + tinybench@2.9.0: + resolution: {integrity: sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg==} + + tinyexec@0.3.2: + resolution: {integrity: sha512-KQQR9yN7R5+OSwaK0XQoj22pwHoTlgYqmUscPYoknOoWCWfj/5/ABTMRi69FrKU5ffPVh5QcFikpWJI/P1ocHA==} + + 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.17: + resolution: {integrity: sha512-DieYoGrP78PWKsrXr8MZwtQ7GLCUeLxihtjC1jZsW1DnvSMdKPitJSe8OSYDM2u5H6g3kWJZpePqkp43TfLh0g==} + + tldts@7.0.17: + resolution: {integrity: sha512-Y1KQBgDd/NUc+LfOtKS6mNsC9CCaH+m2P1RoIZy7RAPo3C3/t8X45+zgut31cRZtZ3xKPjfn3TkGTrctC2TQIQ==} + hasBin: true + + totalist@3.0.1: + resolution: {integrity: sha512-sf4i37nQ2LBx4m3wB74y+ubopq6W/dIzXg0FDGjsYnZHVa1Da8FH853wlL2gtUhg+xJXjfk3kUZS3BRoQeoQBQ==} + engines: {node: '>=6'} + + tough-cookie@6.0.0: + resolution: {integrity: sha512-kXuRi1mtaKMrsLUxz3sQYvVl37B0Ns6MzfrtV5DvJceE9bPyspOqk9xxv7XbZWcfLWbFmm997vl83qUWVJA64w==} + engines: {node: '>=16'} + tr46@0.0.3: resolution: {integrity: sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==} + tr46@6.0.0: + resolution: {integrity: sha512-bLVMLPtstlZ4iMQHpFHTR7GAGj2jxi8Dg0s2h2MafAE4uSWF98FC/3MomU51iQAMf8/qDUbKWf5GxuvvVcXEhw==} + engines: {node: '>=20'} + tslib@2.8.1: resolution: {integrity: sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==} @@ -2068,12 +2932,111 @@ packages: victory-vendor@36.9.2: resolution: {integrity: sha512-PnpQQMuxlwYdocC8fIJqVXvkeViHYzotI+NJrCuav0ZYFoq912ZHBk3mCeuj+5/VpodOjPe1z0Fk2ihgzlXqjQ==} + vite@7.2.0: + resolution: {integrity: sha512-C/Naxf8H0pBx1PA4BdpT+c/5wdqI9ILMdwjSMILw7tVIh3JsxzZqdeTLmmdaoh5MYUEOyBnM9K3o0DzoZ/fe+w==} + engines: {node: ^20.19.0 || >=22.12.0} + hasBin: true + peerDependencies: + '@types/node': ^20.19.0 || >=22.12.0 + jiti: '>=1.21.0' + less: ^4.0.0 + lightningcss: ^1.21.0 + sass: ^1.70.0 + sass-embedded: ^1.70.0 + stylus: '>=0.54.8' + sugarss: ^5.0.0 + terser: ^5.16.0 + tsx: ^4.8.1 + yaml: ^2.4.2 + peerDependenciesMeta: + '@types/node': + optional: true + jiti: + optional: true + less: + optional: true + lightningcss: + optional: true + sass: + optional: true + sass-embedded: + optional: true + stylus: + optional: true + sugarss: + optional: true + terser: + optional: true + tsx: + optional: true + yaml: + optional: true + + vitest@4.0.7: + resolution: {integrity: sha512-xQroKAadK503CrmbzCISvQUjeuvEZzv6U0wlnlVFOi5i3gnzfH4onyQ29f3lzpe0FresAiTAd3aqK0Bi/jLI8w==} + engines: {node: ^20.0.0 || ^22.0.0 || >=24.0.0} + hasBin: true + peerDependencies: + '@edge-runtime/vm': '*' + '@types/debug': ^4.1.12 + '@types/node': ^20.0.0 || ^22.0.0 || >=24.0.0 + '@vitest/browser-playwright': 4.0.7 + '@vitest/browser-preview': 4.0.7 + '@vitest/browser-webdriverio': 4.0.7 + '@vitest/ui': 4.0.7 + happy-dom: '*' + jsdom: '*' + peerDependenciesMeta: + '@edge-runtime/vm': + optional: true + '@types/debug': + 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 + + w3c-xmlserializer@5.0.0: + resolution: {integrity: sha512-o8qghlI8NZHU1lLPrpi2+Uq7abh4GGPpYANlalzWxyWteJOCsr/P+oPBA49TOLu5FTZO4d3F9MnWJfiMo4BkmA==} + engines: {node: '>=18'} + webidl-conversions@3.0.1: resolution: {integrity: sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==} + webidl-conversions@8.0.0: + resolution: {integrity: sha512-n4W4YFyz5JzOfQeA8oN7dUYpR+MBP3PIUsn2jLjWXwK5ASUzt0Jc/A5sAUZoCYFJRGF0FBKJ+1JjN43rNdsQzA==} + engines: {node: '>=20'} + + whatwg-encoding@3.1.1: + resolution: {integrity: sha512-6qN4hJdMwfYBtE3YBTTHhoeuUrDBPZmbQaxWAqSALV/MeEnR5z1xd8UKud2RAkFoPkmB+hli1TZSnyi84xz1vQ==} + engines: {node: '>=18'} + + whatwg-mimetype@4.0.0: + resolution: {integrity: sha512-QaKxh0eNIi2mE9p2vEdzfagOKHCcj1pJ56EEHGQOVxp8r9/iszLUUV7v89x9O1p/T+NlTM5W7jW6+cz4Fq1YVg==} + engines: {node: '>=18'} + + whatwg-url@15.1.0: + resolution: {integrity: sha512-2ytDk0kiEj/yu90JOAp44PVPUkO9+jVhyf+SybKlRHSDlvOOZhdPIrr7xTH64l4WixO2cP+wQIcgujkGBPPz6g==} + engines: {node: '>=20'} + whatwg-url@5.0.0: resolution: {integrity: sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==} + why-is-node-running@2.3.0: + resolution: {integrity: sha512-hUrmaWBdVDcxvYqnyh09zunKzROWjbZTiNy8dBEjkS7ehEDQibXJ7XvlmtbwuTclUiIyN+CyXQD4Vmko8fNm8w==} + engines: {node: '>=8'} + hasBin: true + ws@8.18.3: resolution: {integrity: sha512-PEIGCY5tSlUt50cqyMXfCzX+oOPqN0vuGqWzbcJ2xvnkzkq46oOpz7dQaTDBdfICb4N14+GARUDw2XV2N4tvzg==} engines: {node: '>=10.0.0'} @@ -2086,6 +3049,16 @@ packages: 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@5.0.0: resolution: {integrity: sha512-YgvUTfwqyc7UXVMrB+SImsVYSmTS8X/tSrtdNZMImM+n7+QTriRXyXim0mBrTXNeqzVF0KWGgHPeiyViFFrNDw==} engines: {node: '>=18'} @@ -2095,6 +3068,10 @@ packages: snapshots: + '@acemir/cssom@0.9.19': {} + + '@adobe/css-tools@4.4.4': {} + '@alloc/quick-lru@5.2.0': {} '@ampproject/remapping@2.3.0': @@ -2102,8 +3079,162 @@ snapshots: '@jridgewell/gen-mapping': 0.3.13 '@jridgewell/trace-mapping': 0.3.31 + '@asamuzakjp/css-color@4.0.5': + 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.2 + + '@asamuzakjp/dom-selector@6.7.4': + 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.2 + + '@asamuzakjp/nwsapi@2.3.9': {} + + '@babel/code-frame@7.27.1': + dependencies: + '@babel/helper-validator-identifier': 7.28.5 + js-tokens: 4.0.0 + picocolors: 1.1.1 + + '@babel/compat-data@7.28.5': {} + + '@babel/core@7.28.5': + dependencies: + '@babel/code-frame': 7.27.1 + '@babel/generator': 7.28.5 + '@babel/helper-compilation-targets': 7.27.2 + '@babel/helper-module-transforms': 7.28.3(@babel/core@7.28.5) + '@babel/helpers': 7.28.4 + '@babel/parser': 7.28.5 + '@babel/template': 7.27.2 + '@babel/traverse': 7.28.5 + '@babel/types': 7.28.5 + '@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.5': + dependencies: + '@babel/parser': 7.28.5 + '@babel/types': 7.28.5 + '@jridgewell/gen-mapping': 0.3.13 + '@jridgewell/trace-mapping': 0.3.31 + jsesc: 3.1.0 + + '@babel/helper-compilation-targets@7.27.2': + dependencies: + '@babel/compat-data': 7.28.5 + '@babel/helper-validator-option': 7.27.1 + browserslist: 4.27.0 + lru-cache: 5.1.1 + semver: 6.3.1 + + '@babel/helper-globals@7.28.0': {} + + '@babel/helper-module-imports@7.27.1': + dependencies: + '@babel/traverse': 7.28.5 + '@babel/types': 7.28.5 + transitivePeerDependencies: + - supports-color + + '@babel/helper-module-transforms@7.28.3(@babel/core@7.28.5)': + dependencies: + '@babel/core': 7.28.5 + '@babel/helper-module-imports': 7.27.1 + '@babel/helper-validator-identifier': 7.28.5 + '@babel/traverse': 7.28.5 + transitivePeerDependencies: + - supports-color + + '@babel/helper-plugin-utils@7.27.1': {} + + '@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.4': + dependencies: + '@babel/template': 7.27.2 + '@babel/types': 7.28.5 + + '@babel/parser@7.28.5': + dependencies: + '@babel/types': 7.28.5 + + '@babel/plugin-transform-react-jsx-self@7.27.1(@babel/core@7.28.5)': + dependencies: + '@babel/core': 7.28.5 + '@babel/helper-plugin-utils': 7.27.1 + + '@babel/plugin-transform-react-jsx-source@7.27.1(@babel/core@7.28.5)': + dependencies: + '@babel/core': 7.28.5 + '@babel/helper-plugin-utils': 7.27.1 + '@babel/runtime@7.28.4': {} + '@babel/template@7.27.2': + dependencies: + '@babel/code-frame': 7.27.1 + '@babel/parser': 7.28.5 + '@babel/types': 7.28.5 + + '@babel/traverse@7.28.5': + dependencies: + '@babel/code-frame': 7.27.1 + '@babel/generator': 7.28.5 + '@babel/helper-globals': 7.28.0 + '@babel/parser': 7.28.5 + '@babel/template': 7.27.2 + '@babel/types': 7.28.5 + 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 + + '@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.15': {} + + '@csstools/css-tokenizer@3.0.4': {} + '@date-fns/tz@1.2.0': {} '@emnapi/runtime@1.7.0': @@ -2111,6 +3242,84 @@ snapshots: tslib: 2.8.1 optional: true + '@esbuild/aix-ppc64@0.25.12': + optional: true + + '@esbuild/android-arm64@0.25.12': + optional: true + + '@esbuild/android-arm@0.25.12': + optional: true + + '@esbuild/android-x64@0.25.12': + optional: true + + '@esbuild/darwin-arm64@0.25.12': + optional: true + + '@esbuild/darwin-x64@0.25.12': + optional: true + + '@esbuild/freebsd-arm64@0.25.12': + optional: true + + '@esbuild/freebsd-x64@0.25.12': + optional: true + + '@esbuild/linux-arm64@0.25.12': + optional: true + + '@esbuild/linux-arm@0.25.12': + optional: true + + '@esbuild/linux-ia32@0.25.12': + optional: true + + '@esbuild/linux-loong64@0.25.12': + optional: true + + '@esbuild/linux-mips64el@0.25.12': + optional: true + + '@esbuild/linux-ppc64@0.25.12': + optional: true + + '@esbuild/linux-riscv64@0.25.12': + optional: true + + '@esbuild/linux-s390x@0.25.12': + optional: true + + '@esbuild/linux-x64@0.25.12': + optional: true + + '@esbuild/netbsd-arm64@0.25.12': + optional: true + + '@esbuild/netbsd-x64@0.25.12': + optional: true + + '@esbuild/openbsd-arm64@0.25.12': + optional: true + + '@esbuild/openbsd-x64@0.25.12': + optional: true + + '@esbuild/openharmony-arm64@0.25.12': + optional: true + + '@esbuild/sunos-x64@0.25.12': + optional: true + + '@esbuild/win32-arm64@0.25.12': + optional: true + + '@esbuild/win32-ia32@0.25.12': + optional: true + + '@esbuild/win32-x64@0.25.12': + optional: true + '@floating-ui/core@1.7.3': dependencies: '@floating-ui/utils': 0.2.10 @@ -2227,7 +3436,12 @@ snapshots: '@jridgewell/gen-mapping@0.3.13': dependencies: - '@jridgewell/sourcemap-codec': 1.5.5 + '@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': {} @@ -2269,6 +3483,8 @@ snapshots: dependencies: playwright: 1.56.1 + '@polka/url@1.0.0-next.29': {} + '@radix-ui/number@1.1.0': {} '@radix-ui/number@1.1.1': {} @@ -3176,6 +4392,76 @@ snapshots: '@radix-ui/rect@1.1.1': {} + '@rolldown/pluginutils@1.0.0-beta.43': {} + + '@rollup/rollup-android-arm-eabi@4.52.5': + optional: true + + '@rollup/rollup-android-arm64@4.52.5': + optional: true + + '@rollup/rollup-darwin-arm64@4.52.5': + optional: true + + '@rollup/rollup-darwin-x64@4.52.5': + optional: true + + '@rollup/rollup-freebsd-arm64@4.52.5': + optional: true + + '@rollup/rollup-freebsd-x64@4.52.5': + optional: true + + '@rollup/rollup-linux-arm-gnueabihf@4.52.5': + optional: true + + '@rollup/rollup-linux-arm-musleabihf@4.52.5': + optional: true + + '@rollup/rollup-linux-arm64-gnu@4.52.5': + optional: true + + '@rollup/rollup-linux-arm64-musl@4.52.5': + optional: true + + '@rollup/rollup-linux-loong64-gnu@4.52.5': + optional: true + + '@rollup/rollup-linux-ppc64-gnu@4.52.5': + optional: true + + '@rollup/rollup-linux-riscv64-gnu@4.52.5': + optional: true + + '@rollup/rollup-linux-riscv64-musl@4.52.5': + optional: true + + '@rollup/rollup-linux-s390x-gnu@4.52.5': + optional: true + + '@rollup/rollup-linux-x64-gnu@4.52.5': + optional: true + + '@rollup/rollup-linux-x64-musl@4.52.5': + optional: true + + '@rollup/rollup-openharmony-arm64@4.52.5': + optional: true + + '@rollup/rollup-win32-arm64-msvc@4.52.5': + optional: true + + '@rollup/rollup-win32-ia32-msvc@4.52.5': + optional: true + + '@rollup/rollup-win32-x64-gnu@4.52.5': + optional: true + + '@rollup/rollup-win32-x64-msvc@4.52.5': + optional: true + + '@standard-schema/spec@1.0.0': {} + '@supabase/auth-js@2.78.0': dependencies: '@supabase/node-fetch': 2.6.15 @@ -3304,6 +4590,40 @@ snapshots: postcss: 8.5.0 tailwindcss: 4.1.9 + '@testing-library/dom@10.4.1': + dependencies: + '@babel/code-frame': 7.27.1 + '@babel/runtime': 7.28.4 + '@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.0(@testing-library/dom@10.4.1)(@types/react-dom@19.0.0)(@types/react@19.0.0)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)': + dependencies: + '@babel/runtime': 7.28.4 + '@testing-library/dom': 10.4.1 + react: 19.2.0 + react-dom: 19.2.0(react@19.2.0) + optionalDependencies: + '@types/react': 19.0.0 + '@types/react-dom': 19.0.0 + + '@testing-library/user-event@14.6.1(@testing-library/dom@10.4.1)': + dependencies: + '@testing-library/dom': 10.4.1 + '@testmail.app/graphql-request@1.8.4': dependencies: cross-fetch: 3.2.0 @@ -3311,6 +4631,34 @@ snapshots: transitivePeerDependencies: - encoding + '@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/d3-array@3.2.2': {} '@types/d3-color@3.1.3': {} @@ -3335,6 +4683,10 @@ snapshots: '@types/d3-timer@3.0.2': {} + '@types/deep-eql@4.0.2': {} + + '@types/estree@1.0.8': {} + '@types/node@22.0.0': dependencies: undici-types: 6.11.1 @@ -3353,15 +4705,114 @@ snapshots: dependencies: '@types/node': 22.0.0 - '@vercel/analytics@1.5.0(next@16.0.0(@playwright/test@1.56.1)(react-dom@19.2.0(react@19.2.0))(react@19.2.0))(react@19.2.0)': + '@vercel/analytics@1.5.0(next@16.0.0(@babel/core@7.28.5)(@playwright/test@1.56.1)(react-dom@19.2.0(react@19.2.0))(react@19.2.0))(react@19.2.0)': optionalDependencies: - next: 16.0.0(@playwright/test@1.56.1)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) + next: 16.0.0(@babel/core@7.28.5)(@playwright/test@1.56.1)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) react: 19.2.0 + '@vitejs/plugin-react@5.1.0(vite@7.2.0(@types/node@22.0.0)(jiti@2.6.1)(lightningcss@1.30.1))': + dependencies: + '@babel/core': 7.28.5 + '@babel/plugin-transform-react-jsx-self': 7.27.1(@babel/core@7.28.5) + '@babel/plugin-transform-react-jsx-source': 7.27.1(@babel/core@7.28.5) + '@rolldown/pluginutils': 1.0.0-beta.43 + '@types/babel__core': 7.20.5 + react-refresh: 0.18.0 + vite: 7.2.0(@types/node@22.0.0)(jiti@2.6.1)(lightningcss@1.30.1) + transitivePeerDependencies: + - supports-color + + '@vitest/coverage-v8@4.0.7(vitest@4.0.7)': + dependencies: + '@bcoe/v8-coverage': 1.0.2 + '@vitest/utils': 4.0.7 + ast-v8-to-istanbul: 0.3.8 + debug: 4.4.3 + istanbul-lib-coverage: 3.2.2 + istanbul-lib-report: 3.0.1 + istanbul-lib-source-maps: 5.0.6 + istanbul-reports: 3.2.0 + magicast: 0.3.5 + std-env: 3.10.0 + tinyrainbow: 3.0.3 + vitest: 4.0.7(@types/node@22.0.0)(@vitest/ui@4.0.7)(jiti@2.6.1)(jsdom@27.1.0)(lightningcss@1.30.1) + transitivePeerDependencies: + - supports-color + + '@vitest/expect@4.0.7': + dependencies: + '@standard-schema/spec': 1.0.0 + '@types/chai': 5.2.3 + '@vitest/spy': 4.0.7 + '@vitest/utils': 4.0.7 + chai: 6.2.0 + tinyrainbow: 3.0.3 + + '@vitest/mocker@4.0.7(vite@7.2.0(@types/node@22.0.0)(jiti@2.6.1)(lightningcss@1.30.1))': + dependencies: + '@vitest/spy': 4.0.7 + estree-walker: 3.0.3 + magic-string: 0.30.21 + optionalDependencies: + vite: 7.2.0(@types/node@22.0.0)(jiti@2.6.1)(lightningcss@1.30.1) + + '@vitest/pretty-format@4.0.7': + dependencies: + tinyrainbow: 3.0.3 + + '@vitest/runner@4.0.7': + dependencies: + '@vitest/utils': 4.0.7 + pathe: 2.0.3 + + '@vitest/snapshot@4.0.7': + dependencies: + '@vitest/pretty-format': 4.0.7 + magic-string: 0.30.21 + pathe: 2.0.3 + + '@vitest/spy@4.0.7': {} + + '@vitest/ui@4.0.7(vitest@4.0.7)': + dependencies: + '@vitest/utils': 4.0.7 + fflate: 0.8.2 + flatted: 3.3.3 + pathe: 2.0.3 + sirv: 3.0.2 + tinyglobby: 0.2.15 + tinyrainbow: 3.0.3 + vitest: 4.0.7(@types/node@22.0.0)(@vitest/ui@4.0.7)(jiti@2.6.1)(jsdom@27.1.0)(lightningcss@1.30.1) + + '@vitest/utils@4.0.7': + dependencies: + '@vitest/pretty-format': 4.0.7 + tinyrainbow: 3.0.3 + + agent-base@7.1.4: {} + + ansi-regex@5.0.1: {} + + ansi-styles@5.2.0: {} + aria-hidden@1.2.6: dependencies: tslib: 2.8.1 + 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.8: + dependencies: + '@jridgewell/trace-mapping': 0.3.31 + estree-walker: 3.0.3 + js-tokens: 9.0.1 + autoprefixer@10.4.20(postcss@8.5.0): dependencies: browserslist: 4.27.0 @@ -3374,6 +4825,10 @@ snapshots: baseline-browser-mapping@2.8.23: {} + bidi-js@1.0.3: + dependencies: + require-from-string: 2.0.2 + browserslist@4.27.0: dependencies: baseline-browser-mapping: 2.8.23 @@ -3384,6 +4839,8 @@ snapshots: caniuse-lite@1.0.30001753: {} + chai@6.2.0: {} + chownr@3.0.0: {} class-variance-authority@0.7.1: @@ -3406,6 +4863,8 @@ snapshots: - '@types/react' - '@types/react-dom' + convert-source-map@2.0.0: {} + cookie@1.0.2: {} cross-fetch@3.2.0: @@ -3414,6 +4873,19 @@ snapshots: transitivePeerDependencies: - encoding + 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.2: + dependencies: + '@asamuzakjp/css-color': 4.0.5 + '@csstools/css-syntax-patches-for-csstree': 1.0.15 + css-tree: 3.1.0 + csstype@3.1.3: {} d3-array@3.2.4: @@ -3454,16 +4926,33 @@ snapshots: d3-timer@3.0.1: {} + data-urls@6.0.0: + dependencies: + whatwg-mimetype: 4.0.0 + whatwg-url: 15.1.0 + date-fns-jalali@4.1.0-0: {} date-fns@4.1.0: {} + debug@4.4.3: + dependencies: + ms: 2.1.3 + decimal.js-light@2.5.1: {} + decimal.js@10.6.0: {} + + dequal@2.0.3: {} + detect-libc@2.1.2: {} detect-node-es@1.1.0: {} + dom-accessibility-api@0.5.16: {} + + dom-accessibility-api@0.6.3: {} + dom-helpers@5.2.1: dependencies: '@babel/runtime': 7.28.4 @@ -3488,27 +4977,107 @@ snapshots: graceful-fs: 4.2.11 tapable: 2.3.0 + entities@6.0.1: {} + + es-module-lexer@1.7.0: {} + es6-promise@4.2.8: {} + esbuild@0.25.12: + optionalDependencies: + '@esbuild/aix-ppc64': 0.25.12 + '@esbuild/android-arm': 0.25.12 + '@esbuild/android-arm64': 0.25.12 + '@esbuild/android-x64': 0.25.12 + '@esbuild/darwin-arm64': 0.25.12 + '@esbuild/darwin-x64': 0.25.12 + '@esbuild/freebsd-arm64': 0.25.12 + '@esbuild/freebsd-x64': 0.25.12 + '@esbuild/linux-arm': 0.25.12 + '@esbuild/linux-arm64': 0.25.12 + '@esbuild/linux-ia32': 0.25.12 + '@esbuild/linux-loong64': 0.25.12 + '@esbuild/linux-mips64el': 0.25.12 + '@esbuild/linux-ppc64': 0.25.12 + '@esbuild/linux-riscv64': 0.25.12 + '@esbuild/linux-s390x': 0.25.12 + '@esbuild/linux-x64': 0.25.12 + '@esbuild/netbsd-arm64': 0.25.12 + '@esbuild/netbsd-x64': 0.25.12 + '@esbuild/openbsd-arm64': 0.25.12 + '@esbuild/openbsd-x64': 0.25.12 + '@esbuild/openharmony-arm64': 0.25.12 + '@esbuild/sunos-x64': 0.25.12 + '@esbuild/win32-arm64': 0.25.12 + '@esbuild/win32-ia32': 0.25.12 + '@esbuild/win32-x64': 0.25.12 + escalade@3.2.0: {} + estree-walker@3.0.3: + dependencies: + '@types/estree': 1.0.8 + eventemitter3@4.0.7: {} + expect-type@1.2.2: {} + fast-equals@5.3.2: {} + fdir@6.5.0(picomatch@4.0.3): + optionalDependencies: + picomatch: 4.0.3 + fetch-retry@3.2.3: dependencies: es6-promise: 4.2.8 + fflate@0.8.2: {} + + flatted@3.3.3: {} + fraction.js@4.3.7: {} fsevents@2.3.2: optional: true + fsevents@2.3.3: + optional: true + + gensync@1.0.0-beta.2: {} + get-nonce@1.0.1: {} graceful-fs@4.2.11: {} + has-flag@4.0.0: {} + + html-encoding-sniffer@4.0.0: + dependencies: + whatwg-encoding: 3.1.1 + + 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 + + iconv-lite@0.6.3: + dependencies: + safer-buffer: 2.1.2 + + indent-string@4.0.0: {} + input-otp@1.4.1(react-dom@19.2.0(react@19.2.0))(react@19.2.0): dependencies: react: 19.2.0 @@ -3516,10 +5085,66 @@ snapshots: internmap@2.0.3: {} + 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-lib-source-maps@5.0.6: + dependencies: + '@jridgewell/trace-mapping': 0.3.31 + debug: 4.4.3 + istanbul-lib-coverage: 3.2.2 + transitivePeerDependencies: + - supports-color + + istanbul-reports@3.2.0: + dependencies: + html-escaper: 2.0.2 + istanbul-lib-report: 3.0.1 + jiti@2.6.1: {} js-tokens@4.0.0: {} + js-tokens@9.0.1: {} + + jsdom@27.1.0: + dependencies: + '@acemir/cssom': 0.9.19 + '@asamuzakjp/dom-selector': 6.7.4 + cssstyle: 5.3.2 + data-urls: 6.0.0 + decimal.js: 10.6.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 + 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.0 + whatwg-encoding: 3.1.1 + whatwg-mimetype: 4.0.0 + whatwg-url: 15.1.0 + ws: 8.18.3 + xml-name-validator: 5.0.0 + transitivePeerDependencies: + - bufferutil + - supports-color + - utf-8-validate + + jsesc@3.1.0: {} + + json5@2.2.3: {} + lightningcss-darwin-arm64@1.30.1: optional: true @@ -3571,20 +5196,46 @@ snapshots: dependencies: js-tokens: 4.0.0 + lru-cache@11.2.2: {} + + lru-cache@5.1.1: + dependencies: + yallist: 3.1.1 + lucide-react@0.454.0(react@19.2.0): dependencies: react: 19.2.0 + lz-string@1.5.0: {} + magic-string@0.30.21: dependencies: '@jridgewell/sourcemap-codec': 1.5.5 + magicast@0.3.5: + dependencies: + '@babel/parser': 7.28.5 + '@babel/types': 7.28.5 + source-map-js: 1.2.1 + + make-dir@4.0.0: + dependencies: + semver: 7.7.3 + + mdn-data@2.12.2: {} + + min-indent@1.0.1: {} + minipass@7.1.2: {} minizlib@3.1.0: dependencies: minipass: 7.1.2 + mrmime@2.0.1: {} + + ms@2.1.3: {} + nanoid@3.3.11: {} next-themes@0.4.6(react-dom@19.2.0(react@19.2.0))(react@19.2.0): @@ -3592,7 +5243,7 @@ snapshots: react: 19.2.0 react-dom: 19.2.0(react@19.2.0) - next@16.0.0(@playwright/test@1.56.1)(react-dom@19.2.0(react@19.2.0))(react@19.2.0): + next@16.0.0(@babel/core@7.28.5)(@playwright/test@1.56.1)(react-dom@19.2.0(react@19.2.0))(react@19.2.0): dependencies: '@next/env': 16.0.0 '@swc/helpers': 0.5.15 @@ -3600,7 +5251,7 @@ snapshots: postcss: 8.4.31 react: 19.2.0 react-dom: 19.2.0(react@19.2.0) - styled-jsx: 5.1.6(react@19.2.0) + styled-jsx: 5.1.6(@babel/core@7.28.5)(react@19.2.0) optionalDependencies: '@next/swc-darwin-arm64': 16.0.0 '@next/swc-darwin-x64': 16.0.0 @@ -3626,8 +5277,16 @@ snapshots: object-assign@4.1.1: {} + parse5@8.0.0: + dependencies: + entities: 6.0.1 + + pathe@2.0.3: {} + picocolors@1.1.1: {} + picomatch@4.0.3: {} + playwright-core@1.56.1: {} playwright@1.56.1: @@ -3650,12 +5309,26 @@ snapshots: picocolors: 1.1.1 source-map-js: 1.2.1 + postcss@8.5.6: + dependencies: + nanoid: 3.3.11 + picocolors: 1.1.1 + source-map-js: 1.2.1 + + pretty-format@27.5.1: + dependencies: + ansi-regex: 5.0.1 + ansi-styles: 5.2.0 + react-is: 17.0.2 + prop-types@15.8.1: dependencies: loose-envify: 1.4.0 object-assign: 4.1.1 react-is: 16.13.1 + punycode@2.3.1: {} + react-day-picker@9.8.0(react@19.2.0): dependencies: '@date-fns/tz': 1.2.0 @@ -3674,8 +5347,12 @@ snapshots: react-is@16.13.1: {} + react-is@17.0.2: {} + react-is@18.3.1: {} + react-refresh@0.18.0: {} + react-remove-scroll-bar@2.3.8(@types/react@19.0.0)(react@19.2.0): dependencies: react: 19.2.0 @@ -3744,10 +5421,52 @@ snapshots: tiny-invariant: 1.3.3 victory-vendor: 36.9.2 + redent@3.0.0: + dependencies: + indent-string: 4.0.0 + strip-indent: 3.0.0 + + require-from-string@2.0.2: {} + + rollup@4.52.5: + dependencies: + '@types/estree': 1.0.8 + optionalDependencies: + '@rollup/rollup-android-arm-eabi': 4.52.5 + '@rollup/rollup-android-arm64': 4.52.5 + '@rollup/rollup-darwin-arm64': 4.52.5 + '@rollup/rollup-darwin-x64': 4.52.5 + '@rollup/rollup-freebsd-arm64': 4.52.5 + '@rollup/rollup-freebsd-x64': 4.52.5 + '@rollup/rollup-linux-arm-gnueabihf': 4.52.5 + '@rollup/rollup-linux-arm-musleabihf': 4.52.5 + '@rollup/rollup-linux-arm64-gnu': 4.52.5 + '@rollup/rollup-linux-arm64-musl': 4.52.5 + '@rollup/rollup-linux-loong64-gnu': 4.52.5 + '@rollup/rollup-linux-ppc64-gnu': 4.52.5 + '@rollup/rollup-linux-riscv64-gnu': 4.52.5 + '@rollup/rollup-linux-riscv64-musl': 4.52.5 + '@rollup/rollup-linux-s390x-gnu': 4.52.5 + '@rollup/rollup-linux-x64-gnu': 4.52.5 + '@rollup/rollup-linux-x64-musl': 4.52.5 + '@rollup/rollup-openharmony-arm64': 4.52.5 + '@rollup/rollup-win32-arm64-msvc': 4.52.5 + '@rollup/rollup-win32-ia32-msvc': 4.52.5 + '@rollup/rollup-win32-x64-gnu': 4.52.5 + '@rollup/rollup-win32-x64-msvc': 4.52.5 + fsevents: 2.3.3 + + safer-buffer@2.1.2: {} + + saxes@6.0.0: + dependencies: + xmlchars: 2.2.0 + scheduler@0.27.0: {} - semver@7.7.3: - optional: true + semver@6.3.1: {} + + semver@7.7.3: {} sharp@0.34.4: dependencies: @@ -3779,6 +5498,14 @@ snapshots: '@img/sharp-win32-x64': 0.34.4 optional: true + siginfo@2.0.0: {} + + sirv@3.0.2: + dependencies: + '@polka/url': 1.0.0-next.29 + mrmime: 2.0.1 + totalist: 3.0.1 + sonner@1.7.4(react-dom@19.2.0(react@19.2.0))(react@19.2.0): dependencies: react: 19.2.0 @@ -3786,10 +5513,26 @@ snapshots: source-map-js@1.2.1: {} - styled-jsx@5.1.6(react@19.2.0): + stackback@0.0.2: {} + + std-env@3.10.0: {} + + strip-indent@3.0.0: + dependencies: + min-indent: 1.0.1 + + styled-jsx@5.1.6(@babel/core@7.28.5)(react@19.2.0): dependencies: client-only: 0.0.1 react: 19.2.0 + optionalDependencies: + '@babel/core': 7.28.5 + + supports-color@7.2.0: + dependencies: + has-flag: 4.0.0 + + symbol-tree@3.2.4: {} tailwind-merge@2.5.5: {} @@ -3811,8 +5554,35 @@ snapshots: tiny-invariant@1.3.3: {} + tinybench@2.9.0: {} + + tinyexec@0.3.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.17: {} + + tldts@7.0.17: + dependencies: + tldts-core: 7.0.17 + + totalist@3.0.1: {} + + tough-cookie@6.0.0: + dependencies: + tldts: 7.0.17 + tr46@0.0.3: {} + tr46@6.0.0: + dependencies: + punycode: 2.3.1 + tslib@2.8.1: {} tw-animate-css@1.3.3: {} @@ -3872,15 +5642,97 @@ snapshots: d3-time: 3.1.0 d3-timer: 3.0.1 + vite@7.2.0(@types/node@22.0.0)(jiti@2.6.1)(lightningcss@1.30.1): + dependencies: + esbuild: 0.25.12 + fdir: 6.5.0(picomatch@4.0.3) + picomatch: 4.0.3 + postcss: 8.5.6 + rollup: 4.52.5 + tinyglobby: 0.2.15 + optionalDependencies: + '@types/node': 22.0.0 + fsevents: 2.3.3 + jiti: 2.6.1 + lightningcss: 1.30.1 + + vitest@4.0.7(@types/node@22.0.0)(@vitest/ui@4.0.7)(jiti@2.6.1)(jsdom@27.1.0)(lightningcss@1.30.1): + dependencies: + '@vitest/expect': 4.0.7 + '@vitest/mocker': 4.0.7(vite@7.2.0(@types/node@22.0.0)(jiti@2.6.1)(lightningcss@1.30.1)) + '@vitest/pretty-format': 4.0.7 + '@vitest/runner': 4.0.7 + '@vitest/snapshot': 4.0.7 + '@vitest/spy': 4.0.7 + '@vitest/utils': 4.0.7 + debug: 4.4.3 + es-module-lexer: 1.7.0 + expect-type: 1.2.2 + magic-string: 0.30.21 + pathe: 2.0.3 + picomatch: 4.0.3 + std-env: 3.10.0 + tinybench: 2.9.0 + tinyexec: 0.3.2 + tinyglobby: 0.2.15 + tinyrainbow: 3.0.3 + vite: 7.2.0(@types/node@22.0.0)(jiti@2.6.1)(lightningcss@1.30.1) + why-is-node-running: 2.3.0 + optionalDependencies: + '@types/node': 22.0.0 + '@vitest/ui': 4.0.7(vitest@4.0.7) + jsdom: 27.1.0 + transitivePeerDependencies: + - jiti + - less + - lightningcss + - msw + - sass + - sass-embedded + - stylus + - sugarss + - supports-color + - terser + - tsx + - yaml + + w3c-xmlserializer@5.0.0: + dependencies: + xml-name-validator: 5.0.0 + webidl-conversions@3.0.1: {} + webidl-conversions@8.0.0: {} + + whatwg-encoding@3.1.1: + dependencies: + iconv-lite: 0.6.3 + + whatwg-mimetype@4.0.0: {} + + whatwg-url@15.1.0: + dependencies: + tr46: 6.0.0 + webidl-conversions: 8.0.0 + whatwg-url@5.0.0: dependencies: tr46: 0.0.3 webidl-conversions: 3.0.1 + why-is-node-running@2.3.0: + dependencies: + siginfo: 2.0.0 + stackback: 0.0.2 + ws@8.18.3: {} + xml-name-validator@5.0.0: {} + + xmlchars@2.2.0: {} + + yallist@3.1.1: {} + yallist@5.0.0: {} zod@3.25.76: {} diff --git a/tests/room.spec.ts b/tests/room.spec.ts new file mode 100644 index 0000000..f6ce5a9 --- /dev/null +++ b/tests/room.spec.ts @@ -0,0 +1,99 @@ +import { test, expect } from '@playwright/test'; + +test.describe('Room Management', () => { + + test.beforeEach(async ({ page }) => { + await page.goto('/room'); + }); + + test('should create a new room successfully', async ({ page }) => { + // Click create room button + await page.getByRole('button', { name: 'Create New Room' }).click(); + + // Should show connected room status + await expect(page.locator('text=Connected Room')).toBeVisible({ timeout: 10000 }); + + // Should display an 8-character room ID + const roomIdElement = page.locator('.font-mono.text-3xl'); + await expect(roomIdElement).toBeVisible(); + const roomId = await roomIdElement.textContent(); + expect(roomId).toHaveLength(8); + expect(roomId).toMatch(/^[A-Z0-9]{8}$/); + }); + + test('should show validation error for empty room ID', async ({ page }) => { + // Try to join with empty room ID + await page.getByRole('button', { name: 'Join Room' }).click(); + + // Should show validation or remain on the page + // The button might be disabled or show an error + await expect(page.getByPlaceholder('Enter room ID')).toBeVisible(); + }); + + test('should show validation error for invalid room ID format', async ({ page }) => { + // Enter invalid room ID (less than 8 characters) + await page.getByPlaceholder('Enter room ID').fill('ABC'); + await page.getByRole('button', { name: 'Join Room' }).click(); + + // Should still be on room page (invalid input) + await expect(page.getByPlaceholder('Enter room ID')).toBeVisible(); + }); + + test('should allow leaving a room', async ({ page }) => { + // Create a room first + await page.getByRole('button', { name: 'Create New Room' }).click(); + await expect(page.locator('text=Connected Room')).toBeVisible({ timeout: 10000 }); + + // Find and click leave room button + await page.getByRole('button', { name: 'Leave Room' }).click(); + + // Should return to room selection + await expect(page.getByRole('button', { name: 'Create New Room' })).toBeVisible(); + await expect(page.getByRole('button', { name: 'Join Room' })).toBeVisible(); + }); + + test('should display room ID correctly', async ({ page }) => { + // Create a room + await page.getByRole('button', { name: 'Create New Room' }).click(); + await expect(page.locator('text=Connected Room')).toBeVisible({ timeout: 10000 }); + + // Get the room ID + const roomId = await page.locator('.font-mono.text-3xl').textContent(); + + // Room ID should be visible and copyable + expect(roomId).toBeTruthy(); + expect(roomId).toHaveLength(8); + }); + + test('should handle page refresh in a room', async ({ page }) => { + // Create a room + await page.getByRole('button', { name: 'Create New Room' }).click(); + await expect(page.locator('text=Connected Room')).toBeVisible({ timeout: 10000 }); + + const roomIdBefore = await page.locator('.font-mono.text-3xl').textContent(); + + // Refresh the page + await page.reload(); + + // Should still be in the same room after reload + await expect(page.locator('text=Connected Room')).toBeVisible({ timeout: 10000 }); + const roomIdAfter = await page.locator('.font-mono.text-3xl').textContent(); + + expect(roomIdAfter).toBe(roomIdBefore); + }); + + test('should show user presence in room', async ({ page }) => { + // Create a room + await page.getByRole('button', { name: 'Create New Room' }).click(); + await expect(page.locator('text=Connected Room')).toBeVisible({ timeout: 10000 }); + + // Get current user info + await page.getByRole('button', { name: 'User avatar' }).click(); + const username = await page.locator('.text-sm.font-medium').textContent(); + await page.keyboard.press('Escape'); + + // User should appear in the peer list + await expect(page.locator(`text=${username}`)).toBeVisible(); + }); + +}); diff --git a/tests/setup.ts b/tests/setup.ts new file mode 100644 index 0000000..8008d0e --- /dev/null +++ b/tests/setup.ts @@ -0,0 +1,41 @@ +import { expect, afterEach, vi, beforeAll } from 'vitest' +import { cleanup } from '@testing-library/react' +import * as matchers from '@testing-library/jest-dom/matchers' + +// Extend Vitest's expect with jest-dom matchers +expect.extend(matchers) + +// Cleanup after each test +afterEach(() => { + cleanup() +}) + +// Mock environment variables for tests +process.env.NEXT_PUBLIC_SUPABASE_URL = 'https://test.supabase.co' +process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY = 'test-anon-key' + +// Polyfill Blob.arrayBuffer() for jsdom +beforeAll(() => { + if (!Blob.prototype.arrayBuffer) { + Blob.prototype.arrayBuffer = async function () { + return new Promise((resolve, reject) => { + const reader = new FileReader() + reader.onload = () => resolve(reader.result as ArrayBuffer) + reader.onerror = () => reject(reader.error) + reader.readAsArrayBuffer(this) + }) + } + } +}) + +// Mock Next.js router +vi.mock('next/navigation', () => ({ + useRouter: () => ({ + push: vi.fn(), + replace: vi.fn(), + prefetch: vi.fn(), + back: vi.fn(), + }), + usePathname: () => '/', + useSearchParams: () => new URLSearchParams(), +})) diff --git a/tests/ui.spec.ts b/tests/ui.spec.ts new file mode 100644 index 0000000..cf7b666 --- /dev/null +++ b/tests/ui.spec.ts @@ -0,0 +1,133 @@ +import { test, expect } from '@playwright/test'; + +test.describe('User Interface', () => { + + test.beforeEach(async ({ page }) => { + await page.goto('/room'); + }); + + test('should toggle dark mode', async ({ page }) => { + // Get the theme toggle button + const themeToggle = page.getByLabel('Toggle dark mode'); + await expect(themeToggle).toBeVisible(); + + // Get initial theme (check HTML element) + const htmlElement = page.locator('html'); + const initialClass = await htmlElement.getAttribute('class'); + const isDarkInitially = initialClass?.includes('dark'); + + // Toggle theme + await themeToggle.click(); + + // Wait a moment for theme to update + await page.waitForTimeout(500); + + // Check theme has changed + const newClass = await htmlElement.getAttribute('class'); + const isDarkAfter = newClass?.includes('dark'); + + expect(isDarkAfter).not.toBe(isDarkInitially); + + // Toggle back + await themeToggle.click(); + await page.waitForTimeout(500); + + // Should return to original state + const finalClass = await htmlElement.getAttribute('class'); + const isDarkFinal = finalClass?.includes('dark'); + expect(isDarkFinal).toBe(isDarkInitially); + }); + + test('should persist theme preference across page reloads', async ({ page }) => { + const themeToggle = page.getByLabel('Toggle dark mode'); + const htmlElement = page.locator('html'); + + // Toggle to a specific theme + await themeToggle.click(); + await page.waitForTimeout(500); + + const themeAfterToggle = await htmlElement.getAttribute('class'); + + // Reload page + await page.reload(); + + // Wait for page to fully load + await expect(themeToggle).toBeVisible(); + await page.waitForTimeout(500); + + // Theme should be preserved + const themeAfterReload = await htmlElement.getAttribute('class'); + expect(themeAfterReload).toBe(themeAfterToggle); + }); + + test('should navigate between pages', async ({ page }) => { + // Should be on room page + await expect(page).toHaveURL('/room'); + + // Navigate to profile + await page.getByRole('button', { name: 'User avatar' }).click(); + await page.getByRole('menuitem', { name: 'Profile' }).click(); + + // Should be on profile page + await expect(page).toHaveURL('/profile'); + await expect(page.getByRole('heading', { name: 'Profile' })).toBeVisible(); + + // Navigate back to room + await page.goto('/room'); + await expect(page).toHaveURL('/room'); + }); + + test('should display header on all pages', async ({ page }) => { + // Room page + await expect(page.locator('header')).toBeVisible(); + + // Profile page + await page.goto('/profile'); + await expect(page.locator('header')).toBeVisible(); + }); + + test('should display footer on main page', async ({ page }) => { + await page.goto('/'); + + // Footer should be visible + const footer = page.locator('footer'); + await expect(footer).toBeVisible(); + }); + + test('should show user dropdown menu', async ({ page }) => { + // Click on user avatar + await page.getByRole('button', { name: 'User avatar' }).click(); + + // Menu items should be visible + await expect(page.getByRole('menuitem', { name: 'Profile' })).toBeVisible(); + await expect(page.getByRole('menuitem', { name: 'Sign Out' })).toBeVisible(); + + // Close menu + await page.keyboard.press('Escape'); + + // Menu should be hidden + await expect(page.getByRole('menuitem', { name: 'Profile' })).not.toBeVisible(); + }); + + test('should handle responsive navigation', async ({ page, viewport }) => { + // Test on desktop size + await page.setViewportSize({ width: 1280, height: 720 }); + await expect(page.getByLabel('Toggle dark mode')).toBeVisible(); + + // Test on mobile size + await page.setViewportSize({ width: 375, height: 667 }); + // UI should still be accessible + await expect(page.getByRole('button', { name: 'User avatar' })).toBeVisible(); + }); + + test('should display loading states', async ({ page }) => { + // Create a room + await page.getByRole('button', { name: 'Create New Room' }).click(); + + // Should show some indication of connection + // This might be a spinner or "Connecting..." text + // Wait for successful connection + await expect(page.locator('text=Connected Room')).toBeVisible({ timeout: 10000 }); + }); + +}); diff --git a/tests/unit/lib/utils.test.ts b/tests/unit/lib/utils.test.ts new file mode 100644 index 0000000..1801054 --- /dev/null +++ b/tests/unit/lib/utils.test.ts @@ -0,0 +1,39 @@ +import { describe, it, expect } from 'vitest' +import { cn } from '@/lib/utils' + +describe('cn utility', () => { + it('should merge class names correctly', () => { + const result = cn('foo', 'bar') + expect(result).toBe('foo bar') + }) + + it('should handle conditional classes', () => { + const result = cn('foo', false && 'bar', 'baz') + expect(result).toBe('foo baz') + }) + + it('should merge Tailwind classes correctly', () => { + const result = cn('px-2 py-1', 'px-4') + expect(result).toBe('py-1 px-4') + }) + + it('should handle undefined and null values', () => { + const result = cn('foo', undefined, null, 'bar') + expect(result).toBe('foo bar') + }) + + it('should handle arrays', () => { + const result = cn(['foo', 'bar'], 'baz') + expect(result).toBe('foo bar baz') + }) + + it('should handle objects', () => { + const result = cn({ foo: true, bar: false, baz: true }) + expect(result).toBe('foo baz') + }) + + it('should handle empty input', () => { + const result = cn() + expect(result).toBe('') + }) +}) diff --git a/tests/unit/lib/webrtc/file-transfer.test.ts b/tests/unit/lib/webrtc/file-transfer.test.ts new file mode 100644 index 0000000..0fd75b4 --- /dev/null +++ b/tests/unit/lib/webrtc/file-transfer.test.ts @@ -0,0 +1,218 @@ +import { describe, it, expect, beforeEach, vi } from 'vitest' +import { FileTransferManager, type FileMetadata, type FileChunk } from '@/lib/webrtc/file-transfer' + +describe('FileTransferManager', () => { + let manager: FileTransferManager + + beforeEach(() => { + manager = new FileTransferManager() + }) + + describe('sendFile', () => { + it('should send file metadata and chunks', async () => { + const sendData = vi.fn() + const onProgress = vi.fn() + const fileContent = 'Hello, World!' + const blob = new Blob([fileContent], { type: 'text/plain' }) + const file = new File([blob], 'test.txt', { type: 'text/plain' }) + + await manager.sendFile(file, 'peer123', sendData, onProgress) + + // Should send metadata first + expect(sendData).toHaveBeenCalledWith( + expect.objectContaining({ + type: 'file-metadata', + metadata: expect.objectContaining({ + name: 'test.txt', + size: fileContent.length, + type: 'text/plain', + }), + peerId: 'peer123', + }) + ) + + // Should send chunks + expect(sendData).toHaveBeenCalledWith( + expect.objectContaining({ + type: 'file-chunk', + peerId: 'peer123', + }) + ) + + // Should send completion message + expect(sendData).toHaveBeenCalledWith( + expect.objectContaining({ + type: 'file-complete', + peerId: 'peer123', + }) + ) + + // Should report progress + expect(onProgress).toHaveBeenCalled() + expect(onProgress).toHaveBeenLastCalledWith(100) + }) + + it('should handle large files with multiple chunks', async () => { + const sendData = vi.fn() + const onProgress = vi.fn() + // Create a file larger than chunk size (16KB) + const largeContent = 'x'.repeat(20000) + const blob = new Blob([largeContent], { type: 'text/plain' }) + const file = new File([blob], 'large.txt', { type: 'text/plain' }) + + await manager.sendFile(file, 'peer123', sendData, onProgress) + + // Count chunk messages + const chunkCalls = sendData.mock.calls.filter( + call => call[0].type === 'file-chunk' + ) + expect(chunkCalls.length).toBeGreaterThan(1) + + // Progress should be called multiple times + expect(onProgress.mock.calls.length).toBeGreaterThan(1) + }) + }) + + describe('receiveMetadata', () => { + it('should store metadata for incoming transfer', () => { + const metadata: FileMetadata = { + id: 'file123', + name: 'test.txt', + size: 100, + type: 'text/plain', + } + + manager.receiveMetadata(metadata) + + expect(manager.getMetadata('file123')).toEqual(metadata) + }) + }) + + describe('receiveChunk', () => { + it('should accumulate chunks and report progress', () => { + const metadata: FileMetadata = { + id: 'file123', + name: 'test.txt', + size: 100, + type: 'text/plain', + } + manager.receiveMetadata(metadata) + + const onProgress = vi.fn() + const chunk: FileChunk = { + id: 'file123', + index: 0, + data: new Uint8Array([72, 101, 108, 108, 111]).buffer, + total: 2, + } + + manager.receiveChunk(chunk, onProgress) + + expect(onProgress).toHaveBeenCalledWith('file123', 50) + }) + + it('should handle chunk without metadata gracefully', () => { + const onProgress = vi.fn() + const chunk: FileChunk = { + id: 'nonexistent', + index: 0, + data: new ArrayBuffer(10), + total: 1, + } + + expect(() => { + manager.receiveChunk(chunk, onProgress) + }).not.toThrow() + + expect(onProgress).not.toHaveBeenCalled() + }) + }) + + describe('completeTransfer', () => { + it('should combine chunks and return a blob', () => { + const metadata: FileMetadata = { + id: 'file123', + name: 'test.txt', + size: 10, + type: 'text/plain', + } + manager.receiveMetadata(metadata) + + const chunk1: FileChunk = { + id: 'file123', + index: 0, + data: new Uint8Array([72, 101, 108, 108, 111]).buffer, + total: 2, + } + const chunk2: FileChunk = { + id: 'file123', + index: 1, + data: new Uint8Array([32, 87, 111, 114, 108, 100]).buffer, + total: 2, + } + + manager.receiveChunk(chunk1, vi.fn()) + manager.receiveChunk(chunk2, vi.fn()) + + const blob = manager.completeTransfer('file123') + + expect(blob).toBeInstanceOf(Blob) + expect(blob?.type).toBe('text/plain') + expect(blob?.size).toBeGreaterThan(0) + + // Should clean up after completion + expect(manager.getMetadata('file123')).toBeUndefined() + }) + + it('should return null for nonexistent transfer', () => { + const blob = manager.completeTransfer('nonexistent') + expect(blob).toBeNull() + }) + }) + + describe('cancelTransfer', () => { + it('should remove pending transfer', () => { + const metadata: FileMetadata = { + id: 'file123', + name: 'test.txt', + size: 100, + type: 'text/plain', + } + manager.receiveMetadata(metadata) + + expect(manager.getMetadata('file123')).toBeDefined() + + manager.cancelTransfer('file123') + + expect(manager.getMetadata('file123')).toBeUndefined() + }) + }) + + describe('clearPendingTransfers', () => { + it('should clear all pending transfers', () => { + const metadata1: FileMetadata = { + id: 'file1', + name: 'test1.txt', + size: 100, + type: 'text/plain', + } + const metadata2: FileMetadata = { + id: 'file2', + name: 'test2.txt', + size: 200, + type: 'text/plain', + } + + manager.receiveMetadata(metadata1) + manager.receiveMetadata(metadata2) + + expect(manager.getMetadata('file1')).toBeDefined() + expect(manager.getMetadata('file2')).toBeDefined() + + manager.clearPendingTransfers() + + expect(manager.getMetadata('file1')).toBeUndefined() + expect(manager.getMetadata('file2')).toBeUndefined() + }) + }) +}) diff --git a/vitest.config.ts b/vitest.config.ts new file mode 100644 index 0000000..8f13867 --- /dev/null +++ b/vitest.config.ts @@ -0,0 +1,38 @@ +import { defineConfig } from 'vitest/config' +import react from '@vitejs/plugin-react' +import path from 'path' + +export default defineConfig({ + plugins: [react()], + test: { + environment: 'jsdom', + globals: true, + setupFiles: ['./tests/setup.ts'], + include: ['tests/unit/**/*.{test,spec}.{js,ts,jsx,tsx}'], + exclude: [ + 'node_modules/', + 'tests/*.spec.ts', // Exclude Playwright tests + 'tests/fixtures/**', + 'tests/utils/**', + '.next/', + 'playwright/', + ], + coverage: { + provider: 'v8', + reporter: ['text', 'json', 'html'], + exclude: [ + 'node_modules/', + 'tests/', + '*.config.{ts,js}', + '.next/', + 'playwright/', + 'components/ui/**', // shadcn/ui components + ], + }, + }, + resolve: { + alias: { + '@': path.resolve(__dirname, './'), + }, + }, +}) From 55b1280298bebcb06e47e7c53fe70fe69d8d1ec0 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 5 Nov 2025 15:56:04 +0000 Subject: [PATCH 4/8] Update CI/CD pipeline and add comprehensive testing documentation Co-authored-by: jomzxc <74537369+jomzxc@users.noreply.github.com> --- .github/workflows/ci.yml | 42 ++++++- .gitignore | 5 +- README.md | 25 ++++ TESTING.md | 265 +++++++++++++++++++++++++++++++++++++++ 4 files changed, 333 insertions(+), 4 deletions(-) create mode 100644 TESTING.md diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index b7d8ab9..b23374f 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -35,10 +35,46 @@ jobs: - name: Run linter run: pnpm lint - test: - name: E2E Tests + unit-test: + name: Unit Tests runs-on: ubuntu-latest needs: lint + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Setup pnpm + uses: pnpm/action-setup@v4 + with: + version: 9 + + - name: Setup Node.js + uses: actions/setup-node@v4 + with: + node-version: 20 + cache: "pnpm" + + - name: Install dependencies + run: pnpm install + + - name: Run unit tests + run: pnpm test run + + - name: Generate coverage report + run: pnpm test:coverage run + + - name: Upload coverage report + uses: actions/upload-artifact@v4 + if: always() + with: + name: coverage-report + path: coverage/ + retention-days: 7 + + e2e-test: + name: E2E Tests + runs-on: ubuntu-latest + needs: unit-test # Set environment variables for all test steps env: @@ -85,7 +121,7 @@ jobs: # deploy-dev: # name: Deploy to Development # runs-on: ubuntu-latest - # needs: test + # needs: e2e-test # if: github.ref == 'refs/heads/develop' # steps: # - name: Checkout code diff --git a/.gitignore b/.gitignore index cae8fb7..49a0512 100644 --- a/.gitignore +++ b/.gitignore @@ -29,4 +29,7 @@ next-env.d.ts # Playwright /playwright-report/ /test-results/ -/playwright/.auth/ \ No newline at end of file +/playwright/.auth/ + +# Test coverage +/coverage/ \ No newline at end of file diff --git a/README.md b/README.md index d02fd8d..eedfa97 100644 --- a/README.md +++ b/README.md @@ -82,6 +82,31 @@ Open [http://localhost:3000](http://localhost:3000) in your browser to see the r --- +## ๐Ÿงช Testing + +WebDrop has comprehensive test coverage including unit tests and end-to-end tests. + +### Running Tests + +```bash +# Run unit tests +pnpm test + +# Run E2E tests (requires build first) +pnpm build +pnpm test:e2e + +# Run all tests +pnpm test:all + +# Generate coverage report +pnpm test:coverage +``` + +For detailed testing documentation, see [TESTING.md](TESTING.md). + +--- + ## โš ๏ธ File Size Limit This application is designed to chunk files and send them peer-to-peer. The file chunks are re-assembled in the **receiver's browser memory (RAM)**. diff --git a/TESTING.md b/TESTING.md new file mode 100644 index 0000000..87e45bf --- /dev/null +++ b/TESTING.md @@ -0,0 +1,265 @@ +# Testing Documentation + +This document describes the testing strategy and how to run tests for the WebDrop application. + +## Testing Stack + +- **Unit & Component Tests**: [Vitest](https://vitest.dev/) + [@testing-library/react](https://testing-library.com/react) +- **E2E Tests**: [Playwright](https://playwright.dev/) +- **Coverage**: Vitest Coverage (v8) + +## Test Structure + +``` +tests/ +โ”œโ”€โ”€ unit/ # Unit tests +โ”‚ โ”œโ”€โ”€ lib/ # Library/utility tests +โ”‚ โ”‚ โ”œโ”€โ”€ utils.test.ts +โ”‚ โ”‚ โ””โ”€โ”€ webrtc/ +โ”‚ โ”‚ โ””โ”€โ”€ file-transfer.test.ts +โ”‚ โ””โ”€โ”€ components/ # Component tests (to be added) +โ”œโ”€โ”€ auth.spec.ts # E2E: Authentication tests +โ”œโ”€โ”€ profile.spec.ts # E2E: Profile management tests +โ”œโ”€โ”€ room.spec.ts # E2E: Room management tests +โ”œโ”€โ”€ transfer.spec.ts # E2E: P2P file transfer tests +โ”œโ”€โ”€ ui.spec.ts # E2E: UI/UX tests +โ”œโ”€โ”€ global.setup.ts # Playwright global setup +โ”œโ”€โ”€ setup.ts # Vitest test setup +โ”œโ”€โ”€ fixtures/ # Test fixtures +โ””โ”€โ”€ utils/ # Test utilities +``` + +## Running Tests + +### Unit Tests + +```bash +# Run all unit tests +pnpm test + +# Run tests in watch mode +pnpm test + +# Run tests with UI +pnpm test:ui + +# Run tests with coverage +pnpm test:coverage +``` + +### E2E Tests + +**Prerequisites:** +- Build the application first: `pnpm build` +- Set up environment variables in `.env.local`: + - `NEXT_PUBLIC_SUPABASE_URL` + - `NEXT_PUBLIC_SUPABASE_ANON_KEY` + - `TESTMAIL_API_KEY` (for email-based tests) + - `TESTMAIL_NAMESPACE` (for email-based tests) + +```bash +# Run all E2E tests +pnpm test:e2e + +# Run E2E tests with UI +pnpm test:e2e:ui + +# View last test report +pnpm test:e2e:report +``` + +### Run All Tests + +```bash +# Run both unit and E2E tests +pnpm test:all +``` + +## Test Categories + +### Unit Tests + +#### Utility Tests (`tests/unit/lib/utils.test.ts`) +- Tests the `cn()` utility for merging CSS classes +- Validates conditional class handling +- Tests Tailwind CSS class merging + +#### File Transfer Tests (`tests/unit/lib/webrtc/file-transfer.test.ts`) +- Tests file chunking and sending logic +- Tests metadata handling +- Tests chunk accumulation and progress tracking +- Tests blob assembly from chunks +- Tests transfer cancellation and cleanup + +### E2E Tests + +#### Authentication Tests (`tests/auth.spec.ts`) +- Sign-out flow +- Sign-in with email +- Session persistence + +#### Profile Tests (`tests/profile.spec.ts`) +- Username updates +- Avatar uploads +- Profile persistence + +#### Room Management Tests (`tests/room.spec.ts`) +- Room creation +- Room joining with validation +- Room ID format validation +- Leaving rooms +- Room persistence on page refresh +- User presence display + +#### File Transfer Tests (`tests/transfer.spec.ts`) +- P2P file sending and receiving +- Progress tracking +- Multi-user connection + +#### UI Tests (`tests/ui.spec.ts`) +- Dark mode toggle +- Theme persistence +- Page navigation +- Responsive design +- User dropdown menu +- Loading states + +## Writing Tests + +### Unit Test Example + +```typescript +import { describe, it, expect } from 'vitest' +import { myFunction } from '@/lib/myModule' + +describe('myFunction', () => { + it('should do something', () => { + const result = myFunction('input') + expect(result).toBe('expected output') + }) +}) +``` + +### Component Test Example + +```typescript +import { describe, it, expect } from 'vitest' +import { render, screen } from '@testing-library/react' +import { MyComponent } from '@/components/MyComponent' + +describe('MyComponent', () => { + it('should render correctly', () => { + render() + expect(screen.getByText('Hello')).toBeInTheDocument() + }) +}) +``` + +### E2E Test Example + +```typescript +import { test, expect } from '@playwright/test' + +test('should perform action', async ({ page }) => { + await page.goto('/path') + await page.click('button') + await expect(page.locator('text=Success')).toBeVisible() +}) +``` + +## CI/CD Integration + +Tests are automatically run in GitHub Actions on: +- Push to `main` or `develop` branches +- Pull requests to `main` or `develop` branches + +### CI Pipeline + +1. **Lint** - Code style checks +2. **Unit Tests** - Fast unit and component tests + - Generates coverage report +3. **E2E Tests** - Full integration tests + - Requires Supabase secrets + - Generates Playwright report + +## Coverage Goals + +- **Overall**: Aim for >80% code coverage +- **Critical Paths**: 100% coverage for: + - File transfer logic + - Authentication flows + - Data validation + +## Test Best Practices + +1. **Isolation**: Each test should be independent +2. **Speed**: Keep unit tests fast (<1s each) +3. **Clarity**: Use descriptive test names +4. **Cleanup**: Always clean up after tests +5. **Mocking**: Mock external dependencies in unit tests +6. **Fixtures**: Use test fixtures for consistent data +7. **Assertions**: Make specific, meaningful assertions + +## Debugging Tests + +### Unit Tests + +```bash +# Run specific test file +pnpm test tests/unit/lib/utils.test.ts + +# Run tests matching pattern +pnpm test -t "should merge" + +# Debug with Chrome DevTools +pnpm test --inspect-brk +``` + +### E2E Tests + +```bash +# Run in headed mode +pnpm test:e2e --headed + +# Run specific test file +pnpm test:e2e tests/auth.spec.ts + +# Debug specific test +pnpm test:e2e --debug +``` + +## Common Issues + +### Unit Tests + +**Issue**: `chunk.arrayBuffer is not a function` +**Solution**: Ensure `tests/setup.ts` includes the Blob polyfill + +**Issue**: Module import errors +**Solution**: Check path aliases in `vitest.config.ts` + +### E2E Tests + +**Issue**: Tests timing out +**Solution**: Increase timeout in test or check network connectivity + +**Issue**: Authentication failures +**Solution**: Verify `TESTMAIL_API_KEY` and `TESTMAIL_NAMESPACE` environment variables + +## Future Testing Plans + +- [ ] Add component tests for all React components +- [ ] Add integration tests for API routes +- [ ] Add visual regression tests +- [ ] Add performance tests +- [ ] Add accessibility tests (a11y) +- [ ] Add load/stress tests for file transfers +- [ ] Implement test data factories +- [ ] Add mutation testing + +## Resources + +- [Vitest Documentation](https://vitest.dev/) +- [Testing Library Documentation](https://testing-library.com/) +- [Playwright Documentation](https://playwright.dev/) +- [Next.js Testing Guide](https://nextjs.org/docs/testing) From 588173b40a9558a8f849aa7a4ec8c2a953bdbb90 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 5 Nov 2025 16:00:33 +0000 Subject: [PATCH 5/8] Add example component and hook tests with documentation Co-authored-by: jomzxc <74537369+jomzxc@users.noreply.github.com> --- tests/unit/components/footer.test.tsx | 71 +++++++++++++++++++++++++++ tests/unit/hooks/use-room.test.tsx | 54 ++++++++++++++++++++ 2 files changed, 125 insertions(+) create mode 100644 tests/unit/components/footer.test.tsx create mode 100644 tests/unit/hooks/use-room.test.tsx diff --git a/tests/unit/components/footer.test.tsx b/tests/unit/components/footer.test.tsx new file mode 100644 index 0000000..7171d50 --- /dev/null +++ b/tests/unit/components/footer.test.tsx @@ -0,0 +1,71 @@ +import { describe, it, expect } from 'vitest' +import { render, screen } from '@testing-library/react' +import Footer from '@/components/footer' + +// Example component test for the Footer component +// This demonstrates how to test React components +describe('Footer Component', () => { + it('should render the footer', () => { + render(