;
+ });
+ });
+
+ it('renders register form with all fields', () => {
+ render();
+
+ expect(screen.getByText('Tạo tài khoản')).toBeInTheDocument();
+ expect(screen.getByLabelText('Họ và tên')).toBeInTheDocument();
+ expect(screen.getByLabelText('Số điện thoại')).toBeInTheDocument();
+ expect(screen.getByLabelText(/email/i)).toBeInTheDocument();
+ expect(screen.getByLabelText('Mật khẩu')).toBeInTheDocument();
+ expect(screen.getByLabelText('Xác nhận mật khẩu')).toBeInTheDocument();
+ });
+
+ it('renders login link', () => {
+ render();
+ const loginLink = screen.getByRole('link', { name: /đăng nhập/i });
+ expect(loginLink).toHaveAttribute('href', '/login');
+ });
+
+ it('submits form with valid data', async () => {
+ mockStore.register.mockResolvedValue(undefined);
+ render();
+
+ await userEvent.type(screen.getByLabelText('Họ và tên'), 'Nguyen Van A');
+ await userEvent.type(screen.getByLabelText('Số điện thoại'), '0912345678');
+ await userEvent.type(screen.getByLabelText('Mật khẩu'), 'password123');
+ await userEvent.type(screen.getByLabelText('Xác nhận mật khẩu'), 'password123');
+ await userEvent.click(screen.getByRole('button', { name: /đăng ký/i }));
+
+ await waitFor(() => {
+ expect(mockStore.register).toHaveBeenCalledWith({
+ phone: '0912345678',
+ password: 'password123',
+ fullName: 'Nguyen Van A',
+ email: undefined,
+ });
+ });
+ });
+
+ it('shows validation error for short password', async () => {
+ render();
+
+ await userEvent.type(screen.getByLabelText('Họ và tên'), 'Nguyen Van A');
+ await userEvent.type(screen.getByLabelText('Số điện thoại'), '0912345678');
+ await userEvent.type(screen.getByLabelText('Mật khẩu'), 'short');
+ await userEvent.type(screen.getByLabelText('Xác nhận mật khẩu'), 'short');
+ await userEvent.click(screen.getByRole('button', { name: /đăng ký/i }));
+
+ await waitFor(() => {
+ const alerts = screen.getAllByRole('alert');
+ expect(alerts.length).toBeGreaterThan(0);
+ });
+ });
+
+ it('shows error when passwords do not match', async () => {
+ render();
+
+ await userEvent.type(screen.getByLabelText('Họ và tên'), 'Nguyen Van A');
+ await userEvent.type(screen.getByLabelText('Số điện thoại'), '0912345678');
+ await userEvent.type(screen.getByLabelText('Mật khẩu'), 'password123');
+ await userEvent.type(screen.getByLabelText('Xác nhận mật khẩu'), 'differentpw');
+ await userEvent.click(screen.getByRole('button', { name: /đăng ký/i }));
+
+ await waitFor(() => {
+ const alerts = screen.getAllByRole('alert');
+ expect(alerts.length).toBeGreaterThan(0);
+ });
+ });
+
+ it('displays store error message', () => {
+ mockStore.error = 'Số điện thoại đã tồn tại';
+ render();
+ expect(screen.getByText('Số điện thoại đã tồn tại')).toBeInTheDocument();
+ });
+
+ it('navigates to home after successful registration', async () => {
+ mockStore.register.mockResolvedValue(undefined);
+ render();
+
+ await userEvent.type(screen.getByLabelText('Họ và tên'), 'Nguyen Van A');
+ await userEvent.type(screen.getByLabelText('Số điện thoại'), '0912345678');
+ await userEvent.type(screen.getByLabelText('Mật khẩu'), 'password123');
+ await userEvent.type(screen.getByLabelText('Xác nhận mật khẩu'), 'password123');
+ await userEvent.click(screen.getByRole('button', { name: /đăng ký/i }));
+
+ await waitFor(() => {
+ expect(mockPush).toHaveBeenCalledWith('/');
+ });
+ });
+});
diff --git a/apps/web/app/(dashboard)/listings/__tests__/create-listing.spec.tsx b/apps/web/app/(dashboard)/listings/__tests__/create-listing.spec.tsx
new file mode 100644
index 0000000..38cee0c
--- /dev/null
+++ b/apps/web/app/(dashboard)/listings/__tests__/create-listing.spec.tsx
@@ -0,0 +1,110 @@
+import { render, screen, waitFor } from '@testing-library/react';
+import userEvent from '@testing-library/user-event';
+import { beforeEach, describe, expect, it, vi } from 'vitest';
+
+const mockPush = vi.fn();
+vi.mock('next/navigation', () => ({
+ useRouter: () => ({ push: mockPush }),
+}));
+
+vi.mock('@/lib/listings-api', () => ({
+ listingsApi: {
+ create: vi.fn(),
+ uploadMedia: vi.fn(),
+ },
+}));
+
+vi.mock('@/components/listings/image-upload', () => ({
+ ImageUpload: ({ onChange }: { onChange: (imgs: unknown[]) => void }) => (
+
+
+
+ ),
+}));
+
+import { listingsApi } from '@/lib/listings-api';
+import CreateListingPage from '../new/page';
+
+const mockedListingsApi = vi.mocked(listingsApi);
+
+describe('CreateListingPage', () => {
+ beforeEach(() => {
+ vi.clearAllMocks();
+ });
+
+ it('renders the page title and step indicators', () => {
+ render();
+
+ expect(screen.getByText('Đăng tin mới')).toBeInTheDocument();
+ expect(screen.getByText('Thông tin')).toBeInTheDocument();
+ expect(screen.getByText('Vị trí')).toBeInTheDocument();
+ expect(screen.getByText('Chi tiết')).toBeInTheDocument();
+ expect(screen.getByText('Giá cả')).toBeInTheDocument();
+ expect(screen.getByText('Hình ảnh')).toBeInTheDocument();
+ });
+
+ it('renders step 1 (basic info) initially', () => {
+ render();
+
+ expect(screen.getByText('Thông tin cơ bản')).toBeInTheDocument();
+ expect(screen.getByLabelText(/loại giao dịch/i)).toBeInTheDocument();
+ expect(screen.getByLabelText(/loại bất động sản/i)).toBeInTheDocument();
+ expect(screen.getByLabelText(/tiêu đề/i)).toBeInTheDocument();
+ expect(screen.getByLabelText(/mô tả/i)).toBeInTheDocument();
+ });
+
+ it('has back button disabled on first step', () => {
+ render();
+ expect(screen.getByRole('button', { name: /quay lại/i })).toBeDisabled();
+ });
+
+ it('navigates to step 2 when basic info is filled and next is clicked', async () => {
+ render();
+
+ // Fill step 1
+ await userEvent.selectOptions(screen.getByLabelText(/loại giao dịch/i), 'SALE');
+ await userEvent.selectOptions(screen.getByLabelText(/loại bất động sản/i), 'APARTMENT');
+ await userEvent.type(screen.getByLabelText(/tiêu đề/i), 'Bán căn hộ 2PN tại Quận 7');
+ await userEvent.type(screen.getByLabelText(/mô tả/i), 'Căn hộ view sông tuyệt đẹp, nội thất cao cấp');
+
+ await userEvent.click(screen.getByRole('button', { name: /tiếp theo/i }));
+
+ await waitFor(() => {
+ expect(screen.getByLabelText(/địa chỉ/i)).toBeInTheDocument();
+ });
+ });
+
+ it('shows validation errors when required fields are empty on step 1', async () => {
+ render();
+
+ await userEvent.click(screen.getByRole('button', { name: /tiếp theo/i }));
+
+ // Step should not advance - still showing basic info
+ await waitFor(() => {
+ expect(screen.getByText('Thông tin cơ bản')).toBeInTheDocument();
+ });
+ });
+
+ it('navigates back to previous step', async () => {
+ render();
+
+ // Fill step 1 and go to step 2
+ await userEvent.selectOptions(screen.getByLabelText(/loại giao dịch/i), 'SALE');
+ await userEvent.selectOptions(screen.getByLabelText(/loại bất động sản/i), 'APARTMENT');
+ await userEvent.type(screen.getByLabelText(/tiêu đề/i), 'Test listing title here');
+ await userEvent.type(screen.getByLabelText(/mô tả/i), 'A detailed description of the property for sale');
+
+ await userEvent.click(screen.getByRole('button', { name: /tiếp theo/i }));
+
+ await waitFor(() => {
+ expect(screen.getByLabelText(/địa chỉ/i)).toBeInTheDocument();
+ });
+
+ // Go back
+ await userEvent.click(screen.getByRole('button', { name: /quay lại/i }));
+
+ await waitFor(() => {
+ expect(screen.getByText('Thông tin cơ bản')).toBeInTheDocument();
+ });
+ });
+});
diff --git a/apps/web/app/(public)/search/__tests__/search.spec.tsx b/apps/web/app/(public)/search/__tests__/search.spec.tsx
new file mode 100644
index 0000000..2fb0cf4
--- /dev/null
+++ b/apps/web/app/(public)/search/__tests__/search.spec.tsx
@@ -0,0 +1,140 @@
+import { render, screen, waitFor } from '@testing-library/react';
+import userEvent from '@testing-library/user-event';
+import { beforeEach, describe, expect, it, vi } from 'vitest';
+
+const mockPush = vi.fn();
+const mockReplace = vi.fn();
+const mockSearchParams = new URLSearchParams();
+vi.mock('next/navigation', () => ({
+ useRouter: () => ({ push: mockPush, replace: mockReplace }),
+ useSearchParams: () => mockSearchParams,
+}));
+
+vi.mock('next/link', () => ({
+ default: ({ children, href, ...props }: { children: React.ReactNode; href: string; [key: string]: unknown }) => (
+ {children}
+ ),
+}));
+
+vi.mock('next/image', () => ({
+ default: (props: Record) =>
,
+}));
+
+// Mock dynamic import for map component
+vi.mock('next/dynamic', () => ({
+ default: () => {
+ const MockMap = () => Map
;
+ MockMap.displayName = 'MockMap';
+ return MockMap;
+ },
+}));
+
+const mockListings = {
+ data: [
+ {
+ id: '1',
+ status: 'ACTIVE',
+ transactionType: 'SALE',
+ priceVND: '5000000000',
+ pricePerM2: null,
+ rentPriceMonthly: null,
+ commissionPct: null,
+ viewCount: 10,
+ saveCount: 2,
+ inquiryCount: 1,
+ publishedAt: '2024-01-01',
+ createdAt: '2024-01-01',
+ property: {
+ id: 'p1',
+ propertyType: 'APARTMENT',
+ title: 'Căn hộ Quận 7',
+ description: 'Căn hộ view sông',
+ address: '123 Nguyễn Hữu Thọ',
+ ward: 'Phường Tân Hưng',
+ district: 'Quận 7',
+ city: 'Hồ Chí Minh',
+ areaM2: 75,
+ bedrooms: 2,
+ bathrooms: 2,
+ floors: null,
+ direction: null,
+ yearBuilt: null,
+ legalStatus: null,
+ amenities: null,
+ projectName: null,
+ media: [],
+ },
+ seller: { id: 's1', fullName: 'Nguyen Van A', phone: '0912345678' },
+ agent: null,
+ },
+ ],
+ total: 1,
+ page: 1,
+ limit: 12,
+ totalPages: 1,
+};
+
+vi.mock('@/lib/listings-api', () => ({
+ listingsApi: {
+ search: vi.fn(),
+ },
+}));
+
+import { listingsApi } from '@/lib/listings-api';
+import SearchPage from '../page';
+
+const mockedListingsApi = vi.mocked(listingsApi);
+
+describe('SearchPage', () => {
+ beforeEach(() => {
+ vi.clearAllMocks();
+ mockedListingsApi.search.mockResolvedValue(mockListings as never);
+ });
+
+ it('renders the search page title', async () => {
+ render();
+
+ await waitFor(() => {
+ expect(screen.getByText('Tìm kiếm bất động sản')).toBeInTheDocument();
+ });
+ });
+
+ it('renders view mode toggle buttons', async () => {
+ render();
+
+ await waitFor(() => {
+ expect(screen.getByRole('button', { name: /danh sách/i })).toBeInTheDocument();
+ expect(screen.getByRole('button', { name: /bản đồ/i })).toBeInTheDocument();
+ });
+ });
+
+ it('calls listings API on mount', async () => {
+ render();
+
+ await waitFor(() => {
+ expect(mockedListingsApi.search).toHaveBeenCalled();
+ });
+ });
+
+ it('displays listing results after loading', async () => {
+ render();
+
+ await waitFor(() => {
+ expect(screen.getByText(/căn hộ quận 7/i)).toBeInTheDocument();
+ });
+ });
+
+ it('switches to map view when map button is clicked', async () => {
+ render();
+
+ await waitFor(() => {
+ expect(screen.getByRole('button', { name: /bản đồ/i })).toBeInTheDocument();
+ });
+
+ await userEvent.click(screen.getByRole('button', { name: /bản đồ/i }));
+
+ await waitFor(() => {
+ expect(screen.getByTestId('map-placeholder')).toBeInTheDocument();
+ });
+ });
+});
diff --git a/apps/web/components/ui/__tests__/badge.spec.tsx b/apps/web/components/ui/__tests__/badge.spec.tsx
new file mode 100644
index 0000000..b9a7e98
--- /dev/null
+++ b/apps/web/components/ui/__tests__/badge.spec.tsx
@@ -0,0 +1,35 @@
+import { render, screen } from '@testing-library/react';
+import { describe, expect, it } from 'vitest';
+import { Badge } from '../badge';
+
+describe('Badge', () => {
+ it('renders with text content', () => {
+ render(Active);
+ expect(screen.getByText('Active')).toBeInTheDocument();
+ });
+
+ it('applies default variant styles', () => {
+ render(Default);
+ expect(screen.getByTestId('badge')).toHaveClass('bg-primary');
+ });
+
+ it('applies destructive variant', () => {
+ render(Error);
+ expect(screen.getByTestId('badge')).toHaveClass('bg-destructive');
+ });
+
+ it('applies success variant', () => {
+ render(OK);
+ expect(screen.getByTestId('badge')).toHaveClass('bg-green-100');
+ });
+
+ it('applies warning variant', () => {
+ render(Warn);
+ expect(screen.getByTestId('badge')).toHaveClass('bg-yellow-100');
+ });
+
+ it('applies custom className', () => {
+ render(Custom);
+ expect(screen.getByTestId('badge')).toHaveClass('extra');
+ });
+});
diff --git a/apps/web/components/ui/__tests__/button.spec.tsx b/apps/web/components/ui/__tests__/button.spec.tsx
new file mode 100644
index 0000000..23e1dae
--- /dev/null
+++ b/apps/web/components/ui/__tests__/button.spec.tsx
@@ -0,0 +1,60 @@
+import { render, screen } from '@testing-library/react';
+import userEvent from '@testing-library/user-event';
+import { describe, expect, it, vi } from 'vitest';
+import { Button } from '../button';
+
+describe('Button', () => {
+ it('renders with children text', () => {
+ render();
+ expect(screen.getByRole('button', { name: 'Click me' })).toBeInTheDocument();
+ });
+
+ it('handles click events', async () => {
+ const onClick = vi.fn();
+ render();
+ await userEvent.click(screen.getByRole('button'));
+ expect(onClick).toHaveBeenCalledOnce();
+ });
+
+ it('is disabled when disabled prop is set', () => {
+ render();
+ expect(screen.getByRole('button')).toBeDisabled();
+ });
+
+ it('does not fire click when disabled', async () => {
+ const onClick = vi.fn();
+ render();
+ await userEvent.click(screen.getByRole('button'));
+ expect(onClick).not.toHaveBeenCalled();
+ });
+
+ it('applies variant classes for destructive', () => {
+ render();
+ expect(screen.getByRole('button')).toHaveClass('bg-destructive');
+ });
+
+ it('applies variant classes for outline', () => {
+ render();
+ expect(screen.getByRole('button')).toHaveClass('border');
+ });
+
+ it('applies size classes for sm', () => {
+ render();
+ expect(screen.getByRole('button')).toHaveClass('h-9');
+ });
+
+ it('applies size classes for lg', () => {
+ render();
+ expect(screen.getByRole('button')).toHaveClass('h-11');
+ });
+
+ it('applies custom className', () => {
+ render();
+ expect(screen.getByRole('button')).toHaveClass('custom-class');
+ });
+
+ it('renders as submit button when type is set', () => {
+ render();
+ expect(screen.getByRole('button')).toHaveAttribute('type', 'submit');
+ });
+});
diff --git a/apps/web/components/ui/__tests__/card.spec.tsx b/apps/web/components/ui/__tests__/card.spec.tsx
new file mode 100644
index 0000000..61af027
--- /dev/null
+++ b/apps/web/components/ui/__tests__/card.spec.tsx
@@ -0,0 +1,40 @@
+import { render, screen } from '@testing-library/react';
+import { describe, expect, it } from 'vitest';
+import { Card, CardHeader, CardTitle, CardDescription, CardContent, CardFooter } from '../card';
+
+describe('Card', () => {
+ it('renders card with all sub-components', () => {
+ render(
+
+
+ Title
+ Description
+
+ Content
+ Footer
+ ,
+ );
+
+ expect(screen.getByTestId('card')).toBeInTheDocument();
+ expect(screen.getByText('Title')).toBeInTheDocument();
+ expect(screen.getByText('Description')).toBeInTheDocument();
+ expect(screen.getByText('Content')).toBeInTheDocument();
+ expect(screen.getByText('Footer')).toBeInTheDocument();
+ });
+
+ it('applies custom className to Card', () => {
+ render(Content);
+ expect(screen.getByTestId('card')).toHaveClass('custom');
+ expect(screen.getByTestId('card')).toHaveClass('rounded-lg');
+ });
+
+ it('renders CardTitle as h3', () => {
+ render(My Title);
+ expect(screen.getByRole('heading', { level: 3 })).toHaveTextContent('My Title');
+ });
+
+ it('renders CardDescription as paragraph', () => {
+ render(My Description);
+ expect(screen.getByText('My Description').tagName).toBe('P');
+ });
+});
diff --git a/apps/web/components/ui/__tests__/dialog.spec.tsx b/apps/web/components/ui/__tests__/dialog.spec.tsx
new file mode 100644
index 0000000..6e0b7af
--- /dev/null
+++ b/apps/web/components/ui/__tests__/dialog.spec.tsx
@@ -0,0 +1,71 @@
+import { render, screen } from '@testing-library/react';
+import userEvent from '@testing-library/user-event';
+import { describe, expect, it, vi } from 'vitest';
+import { Dialog, DialogContent, DialogHeader, DialogTitle, DialogDescription, DialogFooter } from '../dialog';
+
+describe('Dialog', () => {
+ it('renders nothing when open is false', () => {
+ render(
+ ,
+ );
+ expect(screen.queryByText('Hidden')).not.toBeInTheDocument();
+ });
+
+ it('renders content when open is true', () => {
+ render(
+ ,
+ );
+
+ expect(screen.getByText('Test Dialog')).toBeInTheDocument();
+ expect(screen.getByText('Dialog description')).toBeInTheDocument();
+ expect(screen.getByText('Body content')).toBeInTheDocument();
+ expect(screen.getByRole('button', { name: 'OK' })).toBeInTheDocument();
+ });
+
+ it('calls onOpenChange when backdrop is clicked', async () => {
+ const onOpenChange = vi.fn();
+ render(
+ ,
+ );
+
+ // Click the backdrop (the overlay div)
+ const backdrop = document.querySelector('.bg-black\\/80');
+ if (backdrop) {
+ await userEvent.click(backdrop);
+ }
+ expect(onOpenChange).toHaveBeenCalledWith(false);
+ });
+
+ it('does not close when clicking inside content', async () => {
+ const onOpenChange = vi.fn();
+ render(
+ ,
+ );
+
+ await userEvent.click(screen.getByText('Stay Open'));
+ expect(onOpenChange).not.toHaveBeenCalled();
+ });
+});
diff --git a/apps/web/components/ui/__tests__/input.spec.tsx b/apps/web/components/ui/__tests__/input.spec.tsx
new file mode 100644
index 0000000..c7a44b2
--- /dev/null
+++ b/apps/web/components/ui/__tests__/input.spec.tsx
@@ -0,0 +1,40 @@
+import { render, screen } from '@testing-library/react';
+import userEvent from '@testing-library/user-event';
+import { describe, expect, it, vi } from 'vitest';
+import { Input } from '../input';
+
+describe('Input', () => {
+ it('renders an input element', () => {
+ render();
+ expect(screen.getByPlaceholderText('Enter text')).toBeInTheDocument();
+ });
+
+ it('accepts and displays typed value', async () => {
+ render();
+ const input = screen.getByPlaceholderText('Type here');
+ await userEvent.type(input, 'Hello');
+ expect(input).toHaveValue('Hello');
+ });
+
+ it('applies type attribute', () => {
+ render();
+ expect(screen.getByPlaceholderText('Email')).toHaveAttribute('type', 'email');
+ });
+
+ it('is disabled when disabled prop is set', () => {
+ render();
+ expect(screen.getByPlaceholderText('Disabled')).toBeDisabled();
+ });
+
+ it('calls onChange handler', async () => {
+ const onChange = vi.fn();
+ render();
+ await userEvent.type(screen.getByPlaceholderText('Input'), 'a');
+ expect(onChange).toHaveBeenCalled();
+ });
+
+ it('applies custom className', () => {
+ render();
+ expect(screen.getByPlaceholderText('Custom')).toHaveClass('my-class');
+ });
+});
diff --git a/apps/web/components/ui/__tests__/label.spec.tsx b/apps/web/components/ui/__tests__/label.spec.tsx
new file mode 100644
index 0000000..4c2680e
--- /dev/null
+++ b/apps/web/components/ui/__tests__/label.spec.tsx
@@ -0,0 +1,25 @@
+import { render, screen } from '@testing-library/react';
+import { describe, expect, it } from 'vitest';
+import { Label } from '../label';
+
+describe('Label', () => {
+ it('renders label text', () => {
+ render();
+ expect(screen.getByText('Số điện thoại')).toBeInTheDocument();
+ });
+
+ it('associates with input via htmlFor', () => {
+ render(
+ <>
+
+
+ >,
+ );
+ expect(screen.getByLabelText('Phone')).toBeInTheDocument();
+ });
+
+ it('applies custom className', () => {
+ render();
+ expect(screen.getByTestId('label')).toHaveClass('custom');
+ });
+});
diff --git a/apps/web/components/ui/__tests__/select.spec.tsx b/apps/web/components/ui/__tests__/select.spec.tsx
new file mode 100644
index 0000000..f2ddd64
--- /dev/null
+++ b/apps/web/components/ui/__tests__/select.spec.tsx
@@ -0,0 +1,40 @@
+import { render, screen } from '@testing-library/react';
+import userEvent from '@testing-library/user-event';
+import { describe, expect, it, vi } from 'vitest';
+import { Select } from '../select';
+
+describe('Select', () => {
+ it('renders with options', () => {
+ render(
+ ,
+ );
+ expect(screen.getByRole('combobox', { name: 'Property type' })).toBeInTheDocument();
+ expect(screen.getAllByRole('option')).toHaveLength(3);
+ });
+
+ it('handles value change', async () => {
+ const onChange = vi.fn();
+ render(
+ ,
+ );
+ await userEvent.selectOptions(screen.getByRole('combobox'), 'SALE');
+ expect(onChange).toHaveBeenCalled();
+ });
+
+ it('is disabled when disabled prop is set', () => {
+ render(
+ ,
+ );
+ expect(screen.getByRole('combobox')).toBeDisabled();
+ });
+});
diff --git a/apps/web/components/ui/__tests__/table.spec.tsx b/apps/web/components/ui/__tests__/table.spec.tsx
new file mode 100644
index 0000000..e1ff698
--- /dev/null
+++ b/apps/web/components/ui/__tests__/table.spec.tsx
@@ -0,0 +1,59 @@
+import { render, screen } from '@testing-library/react';
+import { describe, expect, it } from 'vitest';
+import { Table, TableHeader, TableBody, TableRow, TableHead, TableCell } from '../table';
+
+describe('Table', () => {
+ it('renders a complete table structure', () => {
+ render(
+
+
+
+ Name
+ Price
+
+
+
+
+ Apartment
+ 1,000,000 VND
+
+
+
,
+ );
+
+ expect(screen.getByRole('table')).toBeInTheDocument();
+ expect(screen.getByText('Name')).toBeInTheDocument();
+ expect(screen.getByText('Price')).toBeInTheDocument();
+ expect(screen.getByText('Apartment')).toBeInTheDocument();
+ expect(screen.getByText('1,000,000 VND')).toBeInTheDocument();
+ });
+
+ it('renders multiple rows', () => {
+ render(
+
+
+ Row 1
+ Row 2
+ Row 3
+
+
,
+ );
+
+ expect(screen.getAllByRole('row')).toHaveLength(3);
+ });
+
+ it('applies custom className to table elements', () => {
+ render(
+ ,
+ );
+
+ expect(screen.getByTestId('row')).toHaveClass('highlight');
+ expect(screen.getByTestId('cell')).toHaveClass('bold');
+ });
+});
diff --git a/apps/web/components/ui/__tests__/textarea.spec.tsx b/apps/web/components/ui/__tests__/textarea.spec.tsx
new file mode 100644
index 0000000..f9b2248
--- /dev/null
+++ b/apps/web/components/ui/__tests__/textarea.spec.tsx
@@ -0,0 +1,28 @@
+import { render, screen } from '@testing-library/react';
+import userEvent from '@testing-library/user-event';
+import { describe, expect, it } from 'vitest';
+import { Textarea } from '../textarea';
+
+describe('Textarea', () => {
+ it('renders a textarea element', () => {
+ render();
+ expect(screen.getByPlaceholderText('Mô tả')).toBeInTheDocument();
+ });
+
+ it('accepts typed input', async () => {
+ render();
+ const textarea = screen.getByPlaceholderText('Nhập nội dung');
+ await userEvent.type(textarea, 'Test content');
+ expect(textarea).toHaveValue('Test content');
+ });
+
+ it('is disabled when disabled prop is set', () => {
+ render();
+ expect(screen.getByPlaceholderText('Disabled')).toBeDisabled();
+ });
+
+ it('applies custom className', () => {
+ render();
+ expect(screen.getByPlaceholderText('Custom')).toHaveClass('tall');
+ });
+});
diff --git a/apps/web/lib/__tests__/auth-store.spec.ts b/apps/web/lib/__tests__/auth-store.spec.ts
new file mode 100644
index 0000000..f7573bb
--- /dev/null
+++ b/apps/web/lib/__tests__/auth-store.spec.ts
@@ -0,0 +1,216 @@
+import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
+import { useAuthStore } from '../auth-store';
+import { ApiError } from '../api-client';
+
+// Mock auth-api module
+vi.mock('../auth-api', () => ({
+ authApi: {
+ login: vi.fn(),
+ register: vi.fn(),
+ logout: vi.fn(),
+ refresh: vi.fn(),
+ exchangeToken: vi.fn(),
+ getProfile: vi.fn(),
+ },
+}));
+
+// Import mocked module
+import { authApi } from '../auth-api';
+const mockedAuthApi = vi.mocked(authApi);
+
+const mockUser = {
+ id: '1',
+ email: 'test@example.com',
+ phone: '0912345678',
+ fullName: 'Nguyen Van A',
+ avatarUrl: null,
+ role: 'user',
+ kycStatus: 'pending',
+ isActive: true,
+ createdAt: '2024-01-01',
+};
+
+describe('useAuthStore', () => {
+ beforeEach(() => {
+ // Reset store state
+ useAuthStore.setState({
+ user: null,
+ isAuthenticated: false,
+ isLoading: false,
+ error: null,
+ });
+ vi.clearAllMocks();
+ });
+
+ afterEach(() => {
+ vi.restoreAllMocks();
+ });
+
+ describe('initial state', () => {
+ it('starts with null user and unauthenticated', () => {
+ const state = useAuthStore.getState();
+ expect(state.user).toBeNull();
+ expect(state.isAuthenticated).toBe(false);
+ expect(state.isLoading).toBe(false);
+ expect(state.error).toBeNull();
+ });
+ });
+
+ describe('login', () => {
+ it('sets isAuthenticated and fetches profile on success', async () => {
+ mockedAuthApi.login.mockResolvedValue({ message: 'ok' });
+ mockedAuthApi.getProfile.mockResolvedValue(mockUser);
+
+ await useAuthStore.getState().login({ phone: '0912345678', password: 'pass123' });
+
+ const state = useAuthStore.getState();
+ expect(state.isAuthenticated).toBe(true);
+ expect(state.user).toEqual(mockUser);
+ expect(state.isLoading).toBe(false);
+ expect(state.error).toBeNull();
+ });
+
+ it('sets error on login failure', async () => {
+ mockedAuthApi.login.mockRejectedValue(new ApiError(401, 'Sai mật khẩu'));
+
+ await expect(
+ useAuthStore.getState().login({ phone: '0912345678', password: 'wrong' }),
+ ).rejects.toThrow();
+
+ const state = useAuthStore.getState();
+ expect(state.isAuthenticated).toBe(false);
+ expect(state.error).toBe('Sai mật khẩu');
+ expect(state.isLoading).toBe(false);
+ });
+
+ it('uses default error message for non-ApiError', async () => {
+ mockedAuthApi.login.mockRejectedValue(new Error('Network error'));
+
+ await expect(
+ useAuthStore.getState().login({ phone: '0912345678', password: 'pass' }),
+ ).rejects.toThrow();
+
+ expect(useAuthStore.getState().error).toBe('Đăng nhập thất bại');
+ });
+ });
+
+ describe('register', () => {
+ it('sets isAuthenticated and fetches profile on success', async () => {
+ mockedAuthApi.register.mockResolvedValue({ message: 'ok' });
+ mockedAuthApi.getProfile.mockResolvedValue(mockUser);
+
+ await useAuthStore.getState().register({
+ phone: '0912345678',
+ password: 'password123',
+ fullName: 'Nguyen Van A',
+ });
+
+ const state = useAuthStore.getState();
+ expect(state.isAuthenticated).toBe(true);
+ expect(state.user).toEqual(mockUser);
+ });
+
+ it('sets error on register failure', async () => {
+ mockedAuthApi.register.mockRejectedValue(new ApiError(409, 'Số điện thoại đã tồn tại'));
+
+ await expect(
+ useAuthStore.getState().register({
+ phone: '0912345678',
+ password: 'pass',
+ fullName: 'Test',
+ }),
+ ).rejects.toThrow();
+
+ expect(useAuthStore.getState().error).toBe('Số điện thoại đã tồn tại');
+ });
+ });
+
+ describe('logout', () => {
+ it('clears user and auth state', async () => {
+ useAuthStore.setState({ user: mockUser, isAuthenticated: true });
+ mockedAuthApi.logout.mockResolvedValue({ message: 'ok' });
+
+ await useAuthStore.getState().logout();
+
+ const state = useAuthStore.getState();
+ expect(state.user).toBeNull();
+ expect(state.isAuthenticated).toBe(false);
+ });
+
+ it('clears state even if API logout fails', async () => {
+ useAuthStore.setState({ user: mockUser, isAuthenticated: true });
+ mockedAuthApi.logout.mockRejectedValue(new Error('Network error'));
+
+ await useAuthStore.getState().logout();
+
+ expect(useAuthStore.getState().user).toBeNull();
+ expect(useAuthStore.getState().isAuthenticated).toBe(false);
+ });
+ });
+
+ describe('refreshToken', () => {
+ it('returns true and sets authenticated on success', async () => {
+ mockedAuthApi.refresh.mockResolvedValue({ message: 'ok' });
+
+ const result = await useAuthStore.getState().refreshToken();
+
+ expect(result).toBe(true);
+ expect(useAuthStore.getState().isAuthenticated).toBe(true);
+ });
+
+ it('returns false and clears state on failure', async () => {
+ useAuthStore.setState({ user: mockUser, isAuthenticated: true });
+ mockedAuthApi.refresh.mockRejectedValue(new Error('expired'));
+
+ const result = await useAuthStore.getState().refreshToken();
+
+ expect(result).toBe(false);
+ expect(useAuthStore.getState().user).toBeNull();
+ expect(useAuthStore.getState().isAuthenticated).toBe(false);
+ });
+ });
+
+ describe('fetchProfile', () => {
+ it('fetches and sets user profile', async () => {
+ mockedAuthApi.getProfile.mockResolvedValue(mockUser);
+
+ await useAuthStore.getState().fetchProfile();
+
+ expect(useAuthStore.getState().user).toEqual(mockUser);
+ expect(useAuthStore.getState().isAuthenticated).toBe(true);
+ });
+
+ it('attempts refresh on 401 and retries profile', async () => {
+ mockedAuthApi.getProfile
+ .mockRejectedValueOnce(new ApiError(401, 'Unauthorized'))
+ .mockResolvedValueOnce(mockUser);
+ mockedAuthApi.refresh.mockResolvedValue({ message: 'ok' });
+
+ await useAuthStore.getState().fetchProfile();
+
+ expect(mockedAuthApi.refresh).toHaveBeenCalled();
+ expect(useAuthStore.getState().user).toEqual(mockUser);
+ });
+ });
+
+ describe('handleOAuthCallback', () => {
+ it('exchanges token and fetches profile', async () => {
+ mockedAuthApi.exchangeToken.mockResolvedValue({ message: 'ok' });
+ mockedAuthApi.getProfile.mockResolvedValue(mockUser);
+
+ await useAuthStore.getState().handleOAuthCallback('access', 'refresh', 3600);
+
+ expect(mockedAuthApi.exchangeToken).toHaveBeenCalledWith('access', 'refresh', 3600);
+ expect(useAuthStore.getState().isAuthenticated).toBe(true);
+ expect(useAuthStore.getState().user).toEqual(mockUser);
+ });
+ });
+
+ describe('clearError', () => {
+ it('clears the error state', () => {
+ useAuthStore.setState({ error: 'Some error' });
+ useAuthStore.getState().clearError();
+ expect(useAuthStore.getState().error).toBeNull();
+ });
+ });
+});
diff --git a/apps/web/package.json b/apps/web/package.json
index d24ceed..621b146 100644
--- a/apps/web/package.json
+++ b/apps/web/package.json
@@ -27,11 +27,17 @@
"zustand": "^5.0.12"
},
"devDependencies": {
+ "@testing-library/jest-dom": "^6.9.1",
+ "@testing-library/react": "^16.3.2",
+ "@testing-library/user-event": "^14.6.1",
"@types/mapbox-gl": "^3.5.0",
"@types/node": "^25.5.2",
"@types/react": "^18.3.0",
"@types/react-dom": "^18.3.0",
+ "@vitejs/plugin-react": "^4.7.0",
"autoprefixer": "^10.4.0",
+ "jsdom": "^29.0.2",
+ "msw": "^2.13.2",
"postcss": "^8.4.0",
"tailwindcss": "^3.4.0",
"tailwindcss-animate": "^1.0.7",
diff --git a/apps/web/vitest.config.ts b/apps/web/vitest.config.ts
index b58c7cb..52ad6a8 100644
--- a/apps/web/vitest.config.ts
+++ b/apps/web/vitest.config.ts
@@ -1,9 +1,14 @@
+import react from '@vitejs/plugin-react';
import path from 'path';
import { defineConfig } from 'vitest/config';
export default defineConfig({
+ plugins: [react()],
test: {
- include: ['**/__tests__/**/*.spec.ts', '**/__tests__/**/*.test.ts'],
+ include: ['**/__tests__/**/*.spec.ts', '**/__tests__/**/*.test.ts', '**/__tests__/**/*.spec.tsx', '**/__tests__/**/*.test.tsx'],
+ environment: 'jsdom',
+ setupFiles: ['./vitest.setup.ts'],
+ globals: true,
},
resolve: {
alias: {
diff --git a/apps/web/vitest.setup.ts b/apps/web/vitest.setup.ts
new file mode 100644
index 0000000..bb02c60
--- /dev/null
+++ b/apps/web/vitest.setup.ts
@@ -0,0 +1 @@
+import '@testing-library/jest-dom/vitest';
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index 0cc2d00..46e6dcd 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -243,7 +243,7 @@ importers:
version: 6.0.2
vitest:
specifier: ^4.1.3
- version: 4.1.3(@opentelemetry/api@1.9.1)(@types/node@25.5.2)(vite@7.3.2(@types/node@25.5.2)(jiti@2.6.1)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3))
+ version: 4.1.3(@opentelemetry/api@1.9.1)(@types/node@25.5.2)(jsdom@29.0.2(@noble/hashes@2.0.1))(msw@2.13.2(@types/node@25.5.2)(typescript@6.0.2))(vite@7.3.2(@types/node@25.5.2)(jiti@2.6.1)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3))
apps/web:
dependencies:
@@ -290,6 +290,15 @@ importers:
specifier: ^5.0.12
version: 5.0.12(@types/react@18.3.28)(immer@11.1.4)(react@18.3.1)(use-sync-external-store@1.6.0(react@18.3.1))
devDependencies:
+ '@testing-library/jest-dom':
+ specifier: ^6.9.1
+ version: 6.9.1
+ '@testing-library/react':
+ specifier: ^16.3.2
+ version: 16.3.2(@testing-library/dom@10.4.1)(@types/react-dom@18.3.7(@types/react@18.3.28))(@types/react@18.3.28)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
+ '@testing-library/user-event':
+ specifier: ^14.6.1
+ version: 14.6.1(@testing-library/dom@10.4.1)
'@types/mapbox-gl':
specifier: ^3.5.0
version: 3.5.0
@@ -302,9 +311,18 @@ importers:
'@types/react-dom':
specifier: ^18.3.0
version: 18.3.7(@types/react@18.3.28)
+ '@vitejs/plugin-react':
+ specifier: ^4.7.0
+ version: 4.7.0(vite@7.3.2(@types/node@25.5.2)(jiti@1.21.7)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3))
autoprefixer:
specifier: ^10.4.0
version: 10.4.27(postcss@8.5.8)
+ jsdom:
+ specifier: ^29.0.2
+ version: 29.0.2(@noble/hashes@2.0.1)
+ msw:
+ specifier: ^2.13.2
+ version: 2.13.2(@types/node@25.5.2)(typescript@6.0.2)
postcss:
specifier: ^8.4.0
version: 8.5.8
@@ -319,7 +337,7 @@ importers:
version: 6.0.2
vitest:
specifier: ^4.1.3
- version: 4.1.3(@opentelemetry/api@1.9.1)(@types/node@25.5.2)(vite@7.3.2(@types/node@25.5.2)(jiti@1.21.7)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3))
+ version: 4.1.3(@opentelemetry/api@1.9.1)(@types/node@25.5.2)(jsdom@29.0.2(@noble/hashes@2.0.1))(msw@2.13.2(@types/node@25.5.2)(typescript@6.0.2))(vite@7.3.2(@types/node@25.5.2)(jiti@1.21.7)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3))
libs/mcp-servers:
dependencies:
@@ -347,10 +365,13 @@ importers:
version: 6.0.2
vitest:
specifier: ^4.1.3
- version: 4.1.3(@opentelemetry/api@1.9.1)(@types/node@25.5.2)(vite@7.3.2(@types/node@25.5.2)(jiti@2.6.1)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3))
+ version: 4.1.3(@opentelemetry/api@1.9.1)(@types/node@25.5.2)(jsdom@29.0.2(@noble/hashes@2.0.1))(msw@2.13.2(@types/node@25.5.2)(typescript@6.0.2))(vite@7.3.2(@types/node@25.5.2)(jiti@2.6.1)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3))
packages:
+ '@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'}
@@ -373,6 +394,17 @@ packages:
resolution: {integrity: sha512-Jzs7YM4X6azmHU7Mw5tQSPMuvaqYS8SLnZOJbtiXCy1JyuW9bm/WBBecNHMiuZ8LHXKhvQ6AVX1tKrzF6uiDmw==}
engines: {node: ^18.19.1 || ^20.11.1 || >=22.0.0, npm: ^6.11.0 || ^7.5.6 || >=8.0.0, yarn: '>= 1.13.0'}
+ '@asamuzakjp/css-color@5.1.8':
+ resolution: {integrity: sha512-OISPR9c2uPo23rUdvfEQiLPjoMLOpEeLNnP5iGkxr6tDDxJd3NjD+6fxY0mdaMbIPUjFGL4HFOJqLvow5q4aqQ==}
+ engines: {node: ^20.19.0 || ^22.12.0 || >=24.0.0}
+
+ '@asamuzakjp/dom-selector@7.0.8':
+ resolution: {integrity: sha512-erMO6FgtM02dC24NGm0xufMzWz5OF0wXKR7BpvGD973bq/GbmR8/DbxNZbj0YevQ5hlToJaWSVK/G9/NDgGEVw==}
+ engines: {node: ^20.19.0 || ^22.12.0 || >=24.0.0}
+
+ '@asamuzakjp/nwsapi@2.3.9':
+ resolution: {integrity: sha512-n8GuYSrI9bF7FFZ/SjhwevlHc8xaVlb/7HmHelnc/PZXBD2ZR49NnN9sMMuDdEGPeeRQ5d0hqlSlEpgCX3Wl0Q==}
+
'@aws-crypto/crc32@5.2.0':
resolution: {integrity: sha512-nLbCWqQNgUiwwtFsen1AdzAtvuLRsQS8rYgMuxCrdKf9kOssamGLuPwyTY9wyYblNr9+1XM8v6zoDTPPSIeANg==}
engines: {node: '>=16.0.0'}
@@ -574,6 +606,10 @@ packages:
peerDependencies:
'@babel/core': ^7.0.0
+ '@babel/helper-plugin-utils@7.28.6':
+ resolution: {integrity: sha512-S9gzZ/bz83GRysI7gAD4wPT/AI3uCnY+9xn+Mx/KPs2JwHJIz1W8PZkg2cqyt3RNOBM8ejcXhV6y8Og7ly/Dug==}
+ engines: {node: '>=6.9.0'}
+
'@babel/helper-string-parser@7.27.1':
resolution: {integrity: sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==}
engines: {node: '>=6.9.0'}
@@ -595,6 +631,18 @@ packages:
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.29.2':
resolution: {integrity: sha512-JiDShH45zKHWyGe4ZNVRrCjBz8Nh9TMmZG1kh4QTK8hCBTWBi8Da+i7s1fJw7/lYpM4ccepSNfqzZ/QvABBi5g==}
engines: {node: '>=6.9.0'}
@@ -614,10 +662,50 @@ packages:
'@borewit/text-codec@0.2.2':
resolution: {integrity: sha512-DDaRehssg1aNrH4+2hnj1B7vnUGEjU6OIlyRdkMd0aUdIUvKXrJfXsy8LVtXAy7DRvYVluWbMspsRhz2lcW0mQ==}
+ '@bramus/specificity@2.4.2':
+ resolution: {integrity: sha512-ctxtJ/eA+t+6q2++vj5j7FYX3nRu311q1wfYH3xjlLOsczhlhxAg2FWNUXhpGvAw3BWo1xBcvOV6/YLc2r5FJw==}
+ hasBin: true
+
'@colors/colors@1.5.0':
resolution: {integrity: sha512-ooWCrlZP11i8GImSjTHYHLkvFDP48nS4+204nGb1RiX/WXYHmJA2III9/e2DWVabCESdW7hBAEzHRqUn9OUVvQ==}
engines: {node: '>=0.1.90'}
+ '@csstools/color-helpers@6.0.2':
+ resolution: {integrity: sha512-LMGQLS9EuADloEFkcTBR3BwV/CGHV7zyDxVRtVDTwdI2Ca4it0CCVTT9wCkxSgokjE5Ho41hEPgb8OEUwoXr6Q==}
+ engines: {node: '>=20.19.0'}
+
+ '@csstools/css-calc@3.1.1':
+ resolution: {integrity: sha512-HJ26Z/vmsZQqs/o3a6bgKslXGFAungXGbinULZO3eMsOyNJHeBBZfup5FiZInOghgoM4Hwnmw+OgbJCNg1wwUQ==}
+ engines: {node: '>=20.19.0'}
+ peerDependencies:
+ '@csstools/css-parser-algorithms': ^4.0.0
+ '@csstools/css-tokenizer': ^4.0.0
+
+ '@csstools/css-color-parser@4.0.2':
+ resolution: {integrity: sha512-0GEfbBLmTFf0dJlpsNU7zwxRIH0/BGEMuXLTCvFYxuL1tNhqzTbtnFICyJLTNK4a+RechKP75e7w42ClXSnJQw==}
+ engines: {node: '>=20.19.0'}
+ peerDependencies:
+ '@csstools/css-parser-algorithms': ^4.0.0
+ '@csstools/css-tokenizer': ^4.0.0
+
+ '@csstools/css-parser-algorithms@4.0.0':
+ resolution: {integrity: sha512-+B87qS7fIG3L5h3qwJ/IFbjoVoOe/bpOdh9hAjXbvx0o8ImEmUsGXN0inFOnk2ChCFgqkkGFQ+TpM5rbhkKe4w==}
+ engines: {node: '>=20.19.0'}
+ peerDependencies:
+ '@csstools/css-tokenizer': ^4.0.0
+
+ '@csstools/css-syntax-patches-for-csstree@1.1.2':
+ resolution: {integrity: sha512-5GkLzz4prTIpoyeUiIu3iV6CSG3Plo7xRVOFPKI7FVEJ3mZ0A8SwK0XU3Gl7xAkiQ+mDyam+NNp875/C5y+jSA==}
+ peerDependencies:
+ css-tree: ^3.2.1
+ peerDependenciesMeta:
+ css-tree:
+ optional: true
+
+ '@csstools/css-tokenizer@4.0.0':
+ resolution: {integrity: sha512-QxULHAm7cNu72w97JUNCBFODFaXpbDg+dP8b/oWFAZ2MTRppA3U00Y2L1HqaS4J6yBqxwa/Y3nMBaxVKbB/NsA==}
+ engines: {node: '>=20.19.0'}
+
'@electric-sql/pglite-socket@0.1.1':
resolution: {integrity: sha512-p2hoXw3Z3LQHwTeikdZNsFBOvXGqKY2hk51BBw+8NKND8eoH+8LFOtW9Z8CQKmTJ2qqGYu82ipqiyFZOTTXNfw==}
hasBin: true
@@ -835,6 +923,15 @@ packages:
resolution: {integrity: sha512-43/qtrDUokr7LJqoF2c3+RInu/t4zfrpYdoSDfYyhg52rwLV6TnOvdG4fXm7IkSB3wErkcmJS9iEhjVtOSEjjA==}
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
+ '@exodus/bytes@1.15.0':
+ resolution: {integrity: sha512-UY0nlA+feH81UGSHv92sLEPLCeZFjXOuHhrIo0HQydScuQc8s0A7kL/UdgwgDq8g8ilksmuoF35YVTNphV2aBQ==}
+ engines: {node: ^20.19.0 || ^22.12.0 || >=24.0.0}
+ peerDependencies:
+ '@noble/hashes': ^1.8.0 || ^2.0.0
+ peerDependenciesMeta:
+ '@noble/hashes':
+ optional: true
+
'@fastify/busboy@3.2.0':
resolution: {integrity: sha512-m9FVDXU3GT2ITSe0UaMA5rU3QkfC/UXtCU8y0gSN/GugTqtVldOBWIB5V6V3sbmenVZUIpU6f+mPEO2+m5iTaA==}
@@ -1149,6 +1246,10 @@ packages:
'@cfworker/json-schema':
optional: true
+ '@mswjs/interceptors@0.41.3':
+ resolution: {integrity: sha512-cXu86tF4VQVfwz8W1SPbhoRyHJkti6mjH/XJIxp40jhO4j2k1m4KYrEykxqWPkFF3vrK4rgQppBh//AwyGSXPA==}
+ engines: {node: '>=18'}
+
'@napi-rs/wasm-runtime@0.2.12':
resolution: {integrity: sha512-ZVWUcfwY4E/yPitQJl481FjFo3K22D6qF0DuFH6Y/nbnE11GY5uguDxZMGXPQ8WQ0128MXQD7TnfHyK4oWoIJQ==}
@@ -1364,6 +1465,15 @@ packages:
engines: {node: ^14.18.0 || >=16.10.0, npm: '>=5.10.0'}
hasBin: true
+ '@open-draft/deferred-promise@2.2.0':
+ resolution: {integrity: sha512-CecwLWx3rhxVQF6V4bAgPS5t+So2sTbPgAzafKkVizyi7tlwpcFpdFqq+wqF2OwNBmqFuu6tOyouTuxgpMfzmA==}
+
+ '@open-draft/logger@0.3.0':
+ resolution: {integrity: sha512-X2g45fzhxH238HKO4xbSr7+wBS8Fvw6ixhTDuvLd5mqh6bJJCFAPwU9mPDxbcrRtfxv4u5IHCEH77BmxvXmmxQ==}
+
+ '@open-draft/until@2.1.0':
+ resolution: {integrity: sha512-U69T3ItWHvLwGg5eJ0n3I62nWuE6ilHlmz7zM0npLBRvPRd7e6NYmg54vvRtP5mZG7kZqZCFVdsTWo7BPtBujg==}
+
'@opentelemetry/api-logs@0.207.0':
resolution: {integrity: sha512-lAb0jQRVyleQQGiuuvCOTDVspc14nx6XJjP4FspJ1sNARo3Regq4ZZbrc3rN4b1TYSuUCvgH+UXUPug4SLOqEQ==}
engines: {node: '>=8.0.0'}
@@ -1775,6 +1885,9 @@ packages:
react-redux:
optional: true
+ '@rolldown/pluginutils@1.0.0-beta.27':
+ resolution: {integrity: sha512-+d0F4MKMCbeVUJwG96uQ4SgAznZNSq93I3V+9NHA4OpvqG8mRCpGdKmK8l/dl02h2CCDHwW2FqilnTyDcAnqjA==}
+
'@rollup/plugin-commonjs@28.0.1':
resolution: {integrity: sha512-+tNWdlWKbpB3WgBN7ijjYkq9X5uhjmcvyjEght4NmH5fAU++zfQzAJ6wumLS+dNcvwEZhKx2Z+skY8m7v0wGSA==}
engines: {node: '>=16.0.0 || 14 >= 14.17'}
@@ -2311,6 +2424,35 @@ packages:
'@swc/helpers@0.5.5':
resolution: {integrity: sha512-KGYxvIOXcceOAbEk4bi/dVLEK9z8sZ0uBB3Il5b1rhfClSpcX0yfRO0KmTkqR2cnQDymwLB+25ZyMzICg/cm/A==}
+ '@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.2':
+ resolution: {integrity: sha512-XU5/SytQM+ykqMnAnvB2umaJNIOsLF3PVv//1Ew4CTcpz0/BRyy/af40qqrt7SjKpDdT1saBMc42CUok5gaw+g==}
+ 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'
+
'@tokenizer/inflate@0.4.1':
resolution: {integrity: sha512-2mAv+8pkG6GIZiF1kNg1jAjh27IDxEPKwdGul3snfztFerfPGI1LjDezZp3i7BElXompqEtPmoPx6c2wgtWsOA==}
engines: {node: '>=18'}
@@ -2355,6 +2497,21 @@ packages:
'@tybys/wasm-util@0.10.1':
resolution: {integrity: sha512-9tTaPJLSiejZKx+Bmog4uSubteqTvFrVrURwkmHixBo0G4seD0zUxp98E1DzUBJxLQ3NPwXrGKDiVjwx/DpPsg==}
+ '@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/bcrypt@6.0.0':
resolution: {integrity: sha512-/oJGukuH3D2+D+3H4JWLaAsJ/ji86dhRidzZ/Od7H/i8g+aCmvkeCc6Ni/f9uxGLSQVCRZkX2/lqEFG2BvWtlQ==}
@@ -2522,6 +2679,9 @@ packages:
'@types/serve-static@2.2.0':
resolution: {integrity: sha512-8mam4H1NHLtu7nmtalF7eyBH14QyOASmcxHhSfEoRyr0nP/YdoesEtU+uSRvMe96TW/HPTtkoKqQLl53N7UXMQ==}
+ '@types/statuses@2.0.6':
+ resolution: {integrity: sha512-xMAgYwceFhRA2zY+XbEA7mxYbA093wdiW8Vu6gZPGWy9cmOyU9XesH1tNcEWsKFd5Vzrqx5T3D38PWx1FIIXkA==}
+
'@types/superagent@8.1.9':
resolution: {integrity: sha512-pTVjI73witn+9ILmoJdajHGW2jkSaOzhiFYF1Rd3EQ94kymLqB9PjD9ISg7WaALC7+dCHT0FGe9T2LktLq/3GQ==}
@@ -2697,6 +2857,12 @@ packages:
cpu: [x64]
os: [win32]
+ '@vitejs/plugin-react@4.7.0':
+ resolution: {integrity: sha512-gUu9hwfWvvEDBBmgtAowQCojwZmJ5mcLn3aufeCsitijs3+f2NsrPtlAWIR6OPiqljl96GVCUbLe0HyqIpVaoA==}
+ engines: {node: ^14.18.0 || >=16.0.0}
+ peerDependencies:
+ vite: ^4.2.0 || ^5.0.0 || ^6.0.0 || ^7.0.0
+
'@vitest/expect@4.1.3':
resolution: {integrity: sha512-CW8Q9KMtXDGHj0vCsqui0M5KqRsu0zm0GNDW7Gd3U7nZ2RFpPKSCpeCXoT+/+5zr1TNlsoQRDEz+LzZUyq6gnQ==}
@@ -2883,6 +3049,10 @@ packages:
resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==}
engines: {node: '>=8'}
+ ansi-styles@5.2.0:
+ resolution: {integrity: sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==}
+ engines: {node: '>=10'}
+
ansi-styles@6.2.3:
resolution: {integrity: sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==}
engines: {node: '>=12'}
@@ -2907,6 +3077,13 @@ packages:
argparse@2.0.1:
resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==}
+ 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'}
+
array-timsort@1.0.3:
resolution: {integrity: sha512-/+3GRL7dDAGEfM6TseQk/U+mi18TU2Ms9I3UlLdUMhz2hbvGNTKdj9xniwXfUqgYhHxRx0+8UnKkvlNwVU+cWQ==}
@@ -2971,6 +3148,9 @@ packages:
better-result@2.8.1:
resolution: {integrity: sha512-C4FQ1gCLz1YCxmM8HhNPb4D7WQmdrdllkhNReeLwvIVtJKQFKKfwJwmM3yZEBG4P34cLtrgB+FEPr1u553hF7Q==}
+ bidi-js@1.0.3:
+ resolution: {integrity: sha512-RKshQI1R3YQ+n9YJz2QQ147P66ELpa1FQEg20Dk8oW9t2KgLbpDLLp9aGZ7y8WHSshDknG0bknqGw5/tyCs5tw==}
+
bignumber.js@9.3.1:
resolution: {integrity: sha512-Ko0uX15oIUS7wJ3Rb30Fs6SkVbLmPBAKdlm7q9+ak9bbIeFf0MwuBsQV6z7+X768/cHsfg+WlysDWJcmthjsjQ==}
@@ -3221,6 +3401,10 @@ packages:
resolution: {integrity: sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==}
engines: {node: '>= 0.6'}
+ cookie@1.1.1:
+ resolution: {integrity: sha512-ei8Aos7ja0weRpFzJnEA9UHJ/7XQmqglbRwnf2ATjcB9Wq874VKH9kfjjirM6UhU2/E5fFYadylyhFldcqSidQ==}
+ engines: {node: '>=18'}
+
cookiejar@2.1.4:
resolution: {integrity: sha512-LDx6oHrK+PhzLKJU9j5S7/Y3jM/mUHvD/DeI1WQmJn652iPC5Y4TBzC9l+5OMOXlyTTA+SmVUPm0HQUwpD5Jqw==}
@@ -3241,6 +3425,13 @@ packages:
resolution: {integrity: sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==}
engines: {node: '>= 8'}
+ css-tree@3.2.1:
+ resolution: {integrity: sha512-X7sjQzceUhu1u7Y/ylrRZFU2FS6LRiFVp6rKLPg23y3x3c3DOKAwuXGDp+PAGjh6CSnCjYeAul8pcT8bAl+lSA==}
+ 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==}
+
csscolorparser@1.0.3:
resolution: {integrity: sha512-umPSgYwZkdFoUrH5hIq5kf0wPSXiro51nPw0j2K/c83KflkPSTBGMz6NJvMB+07VlL0y7VPo6QJcDjcgKTTm3w==}
@@ -3300,6 +3491,10 @@ packages:
resolution: {integrity: sha512-0R9ikRb668HB7QDxT1vkpuUBtqc53YyAwMwGeUFKRojY/NWKvdZ+9UYtRfGmhqNbRkTSVpMbmyhXipFFv2cb/A==}
engines: {node: '>= 12'}
+ data-urls@7.0.0:
+ resolution: {integrity: sha512-23XHcCF+coGYevirZceTVD7NdJOqVn+49IHyxgszm+JIiHLoB2TkmPtsYkNWT1pvRSGkc35L6NHs0yHkN2SumA==}
+ engines: {node: ^20.19.0 || ^22.12.0 || >=24.0.0}
+
dateformat@4.6.3:
resolution: {integrity: sha512-2P0p0pFGzHS5EMnhdxQi7aJN+iMheud0UhG4dlE1DLAlvL8JHjJJTX/CSm4JXwV0Ka5nGk3zC5mcb5bUQUxxMA==}
@@ -3315,6 +3510,9 @@ packages:
decimal.js-light@2.5.1:
resolution: {integrity: sha512-qIMFpTMZmny+MMIitAB6D7iVPEorVw6YQRWkvarTkT4tBeSLLiHzcwj6q0MmYSFCiVpiqPJTJEYIrpcPzVEIvg==}
+ decimal.js@10.6.0:
+ resolution: {integrity: sha512-YpgQiITW3JXGntzdUmyUR1V812Hn8T1YVXhCu+wO3OpS4eU9l4YdD3qjyiKdV6mvV29zapkMeD390UVEf2lkUg==}
+
deep-is@0.1.4:
resolution: {integrity: sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==}
@@ -3349,6 +3547,10 @@ packages:
engines: {node: ^20.12||^22||>=24}
hasBin: true
+ dequal@2.0.3:
+ resolution: {integrity: sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==}
+ engines: {node: '>=6'}
+
destr@2.0.5:
resolution: {integrity: sha512-ugFTXCtDZunbzasqBxrK93Ik/DRYsO6S/fedkWEMKqt04xZ4csmnmwGDBAb07QWNaGMAmnTIemsYZCksjATwsA==}
@@ -3365,6 +3567,12 @@ packages:
dlv@1.1.3:
resolution: {integrity: sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA==}
+ dom-accessibility-api@0.5.16:
+ resolution: {integrity: sha512-X7BJ2yElsnOJ30pZF4uIIDfBEVgF4XEBxL9Bxhy6dnrm5hkzqmsWHGTiHqRiITNhMyFLyAiWndIJP7Z1NTteDg==}
+
+ dom-accessibility-api@0.6.3:
+ resolution: {integrity: sha512-7ZgogeTnjuHbo+ct10G9Ffp0mif17idi0IyWNVA/wcwcm7NPOD/WEHVP3n7n3MhXqxoIYm8d6MuZohYWIZ4T3w==}
+
dom-serializer@2.0.0:
resolution: {integrity: sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg==}
@@ -3433,6 +3641,10 @@ packages:
resolution: {integrity: sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==}
engines: {node: '>=0.12'}
+ entities@6.0.1:
+ resolution: {integrity: sha512-aN97NXWF6AWBTahfVOIrB/NShkzi5H7F9r1s9mD3cDj4Ko5f2qhhVoYMibXF7GlLveb/D2ioWay8lxI97Ven3g==}
+ engines: {node: '>=0.12'}
+
entities@7.0.1:
resolution: {integrity: sha512-TWrgLOFUQTH994YUyl1yT4uyavY5nNB5muff+RtWaqNVCAK408b5ZnnbNAUEWLTCpum9w6arT70i1XdQ4UeOPA==}
engines: {node: '>=0.12'}
@@ -3921,6 +4133,10 @@ packages:
graphmatch@1.1.1:
resolution: {integrity: sha512-5ykVn/EXM1hF0XCaWh05VbYvEiOL2lY1kBxZtaYsyvjp7cmWOU1XsAdfQBwClraEofXDT197lFbXOEVMHpvQOg==}
+ graphql@16.13.2:
+ resolution: {integrity: sha512-5bJ+nf/UCpAjHM8i06fl7eLyVC9iuNAjm9qzkiu2ZGhM0VscSvS6WDPfAwkdkBuoXGM9FJSbKl6wylMwP9Ktig==}
+ engines: {node: ^12.22.0 || ^14.16.0 || ^16.0.0 || >=17.0.0}
+
grid-index@1.1.0:
resolution: {integrity: sha512-HZRwumpOGUrHyxO5bqKZL0B0GlUpwtCAzZ42sgxUPniu33R1LSFH5yrIcBCHjkctCAh3mtWKcKd9J4vDDdeVHA==}
@@ -3949,6 +4165,9 @@ packages:
resolution: {integrity: sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==}
engines: {node: '>= 0.4'}
+ headers-polyfill@4.0.3:
+ resolution: {integrity: sha512-IScLbePpkvO846sIwOtOTDjutRMWdXdJmXdMvk6gCBHxFO8d+QKOQedyZSxFTTFYRSmlgSTDtXqqq4pcenBXLQ==}
+
helmet@8.1.0:
resolution: {integrity: sha512-jOiHyAZsmnr8LqoPGmCjYAaiuWwjAPLgY8ZX2XrmHawt99/u1y6RgrZMTeoPfpUbV96HOalYgz1qzkRbw54Pmg==}
engines: {node: '>=18.0.0'}
@@ -3960,6 +4179,10 @@ packages:
resolution: {integrity: sha512-p1JfQMKaceuCbpJKAPKVqyqviZdS0eUxH9v82oWo1kb9xjQ5wA6iP3FNVAPDFlz5/p7d45lO+BpSk1tuSZMF4Q==}
engines: {node: '>=16.9.0'}
+ html-encoding-sniffer@6.0.0:
+ resolution: {integrity: sha512-CV9TW3Y3f8/wT0BRFc1/KAVQ3TUHiXmaAb6VW9vtiMFf7SLoMd1PdAc4W3KFOFETBJUb90KatHqlsZMWV+R9Gg==}
+ engines: {node: ^20.19.0 || ^22.12.0 || >=24.0.0}
+
html-entities@2.6.0:
resolution: {integrity: sha512-kig+rMn/QOVRvr7c86gQ8lWXq+Hkv6CbAH1hLu+RG338StTpE8Z0b44SDVaqVu7HGKf27frdmUYEs9hTUX/cLQ==}
@@ -4029,6 +4252,10 @@ packages:
resolution: {integrity: sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==}
engines: {node: '>=0.8.19'}
+ indent-string@4.0.0:
+ resolution: {integrity: sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==}
+ engines: {node: '>=8'}
+
inherits@2.0.4:
resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==}
@@ -4094,6 +4321,9 @@ packages:
resolution: {integrity: sha512-2HvIEKRoqS62guEC+qBjpvRubdX910WCMuJTZ+I9yvqKU2/12eSL549HMwtabb4oupdj2sMP50k+XJfB/8JE6w==}
engines: {node: '>=8'}
+ is-node-process@1.2.0:
+ resolution: {integrity: sha512-Vg4o6/fqPxIjtxgUH5QLJhwZ7gW5diGCVlXpuUfELC62CuxM1iHcRe51f2W1FDy04Ai4KJkagKjx3XaqyfRKXw==}
+
is-number@7.0.0:
resolution: {integrity: sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==}
engines: {node: '>=0.12.0'}
@@ -4106,6 +4336,9 @@ packages:
resolution: {integrity: sha512-VRSzKkbMm5jMDoKLbltAkFQ5Qr7VDiTFGXxYFXXowVj387GeGNOCsOH6Msy00SGZ3Fp84b1Naa1psqgcCIEP5Q==}
engines: {node: '>=0.10.0'}
+ is-potential-custom-element-name@1.0.1:
+ resolution: {integrity: sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ==}
+
is-promise@4.0.0:
resolution: {integrity: sha512-hvpoI6korhJMnej285dSg6nu1+e6uxs7zG3BYAm5byqDsgJNWwxzM6z6iZiAgQR4TJ30JmBTOwqZUw3WlyH3AQ==}
@@ -4159,6 +4392,15 @@ packages:
resolution: {integrity: sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==}
hasBin: true
+ jsdom@29.0.2:
+ resolution: {integrity: sha512-9VnGEBosc/ZpwyOsJBCQ/3I5p7Q5ngOY14a9bf5btenAORmZfDse1ZEheMiWcJ3h81+Fv7HmJFdS0szo/waF2w==}
+ engines: {node: ^20.19.0 || ^22.13.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'}
@@ -4342,6 +4584,10 @@ packages:
peerDependencies:
react: ^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0
+ lz-string@1.5.0:
+ resolution: {integrity: sha512-h5bgJWpxJNswbU7qCrV0tIKQCaS3blPDrqKWx+QxzuzL1zGUzij9XCWLrSLsJPu5t+eWA/ycetzYAO5IOMcWAQ==}
+ hasBin: true
+
magic-string@0.30.17:
resolution: {integrity: sha512-sNPKHvyjVf7gyjwS4xGTaW/mCnF8wnjtifKBEhxfZ7E/S8tQ0rssrwGNn6q8JH/ohItJfSQp9mBtQYuTlH5QnA==}
@@ -4358,6 +4604,9 @@ packages:
resolution: {integrity: sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==}
engines: {node: '>= 0.4'}
+ mdn-data@2.27.1:
+ resolution: {integrity: sha512-9Yubnt3e8A0OKwxYSXyhLymGW4sCufcLG6VdiDdUGVkPhpqLxlvP5vl1983gQjJl3tqbrM731mjaZaP68AgosQ==}
+
media-typer@0.3.0:
resolution: {integrity: sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==}
engines: {node: '>= 0.6'}
@@ -4423,6 +4672,10 @@ packages:
resolution: {integrity: sha512-VP79XUPxV2CigYP3jWwAUFSku2aKqBH7uTAapFWCBqutsbmDo96KY5o8uh6U+/YSIn5OxJnXp73beVkpqMIGhA==}
engines: {node: '>=18'}
+ min-indent@1.0.1:
+ resolution: {integrity: sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg==}
+ engines: {node: '>=4'}
+
minimatch@10.2.5:
resolution: {integrity: sha512-MULkVLfKGYDFYejP07QOurDLLQpcjk7Fw+7jXS2R2czRQzR56yHRveU5NDJEOviH+hETZKSkIk5c+T23GjFUMg==}
engines: {node: 18 || 20 || >=22}
@@ -4443,6 +4696,16 @@ packages:
ms@2.1.3:
resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==}
+ msw@2.13.2:
+ resolution: {integrity: sha512-go2H1TIERKkC48pXiwec5l6sbNqYuvqOk3/vHGo1Zd+pq/H63oFawDQerH+WQdUw/flJFHDG7F+QdWMwhntA/A==}
+ engines: {node: '>=18'}
+ hasBin: true
+ peerDependencies:
+ typescript: '>= 4.8.x'
+ peerDependenciesMeta:
+ typescript:
+ optional: true
+
multer@2.1.1:
resolution: {integrity: sha512-mo+QTzKlx8R7E5ylSXxWzGoXoZbOsRMpyitcht8By2KHvMbf3tjwosZ/Mu/XYU6UuJ3VZnODIrak5ZrPiPyB6A==}
engines: {node: '>= 10.16.0'}
@@ -4610,6 +4873,9 @@ packages:
resolution: {integrity: sha512-5b6Y85tPxZZ7QytO+BQzysW31HJku27cRIlkbAXaNx+BdcVi+LlRFmVXzeF6a7JCwJpyw5c4b+YSVImQIrBpuQ==}
engines: {node: '>=10'}
+ outvariant@1.4.3:
+ resolution: {integrity: sha512-+Sl2UErvtsoajRDKCE5/dBz4DIvHXQQnAxtQTF04OJxY0+DyZXSo5P5Bb7XYWOh81syohlYL24hbDwxedPUJCA==}
+
p-limit@3.1.0:
resolution: {integrity: sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==}
engines: {node: '>=10'}
@@ -4629,6 +4895,9 @@ packages:
parse-srcset@1.0.2:
resolution: {integrity: sha512-/2qh0lav6CmI15FzA3i/2Bzk2zCgQhGMkvhOhKNcBVQ1ldgpbfiNTVslmooUmWJcADi1f1kIeynbDRVzNlfR6Q==}
+ parse5@8.0.0:
+ resolution: {integrity: sha512-9m4m5GSgXjL4AjumKzq1Fgfp3Z8rsvjRNbnkVwfu2ImRqE5D0LnY2QfDen18FSY9C573YU5XxSapdHZTZ2WolA==}
+
parseurl@1.3.3:
resolution: {integrity: sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==}
engines: {node: '>= 0.8'}
@@ -4675,6 +4944,9 @@ packages:
resolution: {integrity: sha512-3O/iVVsJAPsOnpwWIeD+d6z/7PmqApyQePUtCndjatj/9I5LylHvt5qluFaBT3I5h3r1ejfR056c+FCv+NnNXg==}
engines: {node: 18 || 20 || >=22}
+ path-to-regexp@6.3.0:
+ resolution: {integrity: sha512-Yhpw4T9C6hPpgPeA28us07OJeqZ5EzQTkbfwuhsUg0c237RomFoETJgmp2sa3F/41gfLE6G5cqcYwznmeEeOlQ==}
+
path-to-regexp@8.3.0:
resolution: {integrity: sha512-7jdwVIRtsP8MYpdXSwOS0YdD0Du+qOoF/AEPIt88PcCFrZCzx41oxku1jD88hZBwbNUIEfpqvuhjFaMAqMTWnA==}
@@ -4877,6 +5149,10 @@ packages:
engines: {node: '>=14'}
hasBin: true
+ pretty-format@27.5.1:
+ resolution: {integrity: sha512-Qb1gy5OrP5+zDf2Bvnzdl3jsTf1qXVMazbvCoKhtKqVs4/YK4ozX4gKQJJVyNe+cajNPn0KoC0MC3FUmaHWEmQ==}
+ engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0}
+
prisma@7.7.0:
resolution: {integrity: sha512-HlgwRBt1uEFB9LStHL4HLYDvoi4BNu1rYA0hPG0zCAEyK9SaZBqp7E5Rjpc3Qh8Lex/ye/svoHZ0OWoFNhWxuQ==}
engines: {node: ^20.19 || ^22.12 || >=24.0}
@@ -4971,6 +5247,9 @@ packages:
peerDependencies:
react: ^16.8.0 || ^17 || ^18 || ^19
+ react-is@17.0.2:
+ resolution: {integrity: sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==}
+
react-is@19.2.4:
resolution: {integrity: sha512-W+EWGn2v0ApPKgKKCy/7s7WHXkboGcsrXE+2joLyVxkbyVQfO3MUEaUQDHoSmb8TFFrSKYa9mw64WZHNHSDzYA==}
@@ -4986,6 +5265,10 @@ packages:
redux:
optional: true
+ react-refresh@0.17.0:
+ resolution: {integrity: sha512-z6F7K9bV85EfseRCp2bzrpyQ0Gkw1uLoCel9XBVWPg/TjRj94SkJzUTGfOa4bs7iJvBWtQG0Wq7wnI0syw3EBQ==}
+ engines: {node: '>=0.10.0'}
+
react@18.3.1:
resolution: {integrity: sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ==}
engines: {node: '>=0.10.0'}
@@ -5021,6 +5304,10 @@ packages:
resolution: {integrity: sha512-/vxpCXddiX8NGfGO/mTafwjq4aFa/71pvamip0++IQk3zG8cbCj0fifNPrjjF1XMXUne91jL9OoxmdykoEtifQ==}
engines: {node: '>= 10.13.0'}
+ redent@3.0.0:
+ resolution: {integrity: sha512-6tDA8g98We0zd0GvVeMT9arEOnTw9qM03L9cJXaCjrip1OO764RDBLBfrB4cwzNGDj5OA5ioymC9GkizgWJDUg==}
+ engines: {node: '>=8'}
+
redis-errors@1.2.0:
resolution: {integrity: sha512-1qny3OExCf0UvUV/5wpYKf2YwPcOqXzkwKKSmKHiE6ZMQs5heeE/c8eXK+PNllPvmjgAbfnsbpkGZWy8cBpn9w==}
engines: {node: '>=4'}
@@ -5097,6 +5384,9 @@ packages:
resolution: {integrity: sha512-XQBQ3I8W1Cge0Seh+6gjj03LbmRFWuoszgK9ooCpwYIrhhoO80pfq4cUkU5DkknwfOfFteRwlZ56PYOGYyFWdg==}
engines: {node: '>= 4'}
+ rettime@0.10.1:
+ resolution: {integrity: sha512-uyDrIlUEH37cinabq0AX4QbgV4HbFZ/gqoiunWQ1UqBtRvTTytwhNYjE++pO/MjPTZL5KQCf2bEoJ/BJNVQ5Kw==}
+
reusify@1.1.0:
resolution: {integrity: sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==}
engines: {iojs: '>=1.0.0', node: '>=0.10.0'}
@@ -5141,6 +5431,10 @@ packages:
sanitize-html@2.17.2:
resolution: {integrity: sha512-EnffJUl46VE9uvZ0XeWzObHLurClLlT12gsOk1cHyP2Ol1P0BnBnsXmShlBmWVJM+dKieQI68R0tsPY5m/B+Jg==}
+ saxes@6.0.0:
+ resolution: {integrity: sha512-xAg7SOnEhrm5zI3puOOKyy1OMcMlIJZYNJY7xLBwSze0UjhPLnWfj2GF2EpT0jmzaJKIWKHLsaSSajf35bcYnA==}
+ engines: {node: '>=v12.22.7'}
+
scheduler@0.23.2:
resolution: {integrity: sha512-UOShsPwz7NrMUqhR6t0hWjFduvOzbtv7toDH1/hIrfRNIDBnnBWd0CwJTGvTpngVlmwGCdP9/Zl/tVrDqcuYzQ==}
@@ -5286,6 +5580,9 @@ packages:
resolution: {integrity: sha512-Mcc5wHehp9aXz1ax6bZUyY5afg9u2rv5cqQI3mRrYkGC8rW2hM02jWuwjtL++LS5qinSyhj2QfLyNsuc+VsExg==}
engines: {node: '>=10.0.0'}
+ strict-event-emitter@0.5.1:
+ resolution: {integrity: sha512-vMgjE/GGEPEFnhFub6pa4FmJBRBVOLpIII2hvCZ8Kzb7K0hlHo7mQv6xYrBvCL2LtAIBwFUK8wvuJgTVSQ5MFQ==}
+
string-argv@0.3.2:
resolution: {integrity: sha512-aqD2Q0144Z+/RqG52NeHEkZauTAUWJO8c6yTftGJKO3Tja5tUgIfmIl6kExvhtxSDP7fXB6DvzkfMpCd/F3G+Q==}
engines: {node: '>=0.6.19'}
@@ -5317,6 +5614,10 @@ packages:
resolution: {integrity: sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==}
engines: {node: '>=4'}
+ strip-indent@3.0.0:
+ resolution: {integrity: sha512-laJTa3Jb+VQpaC6DseHhF7dXVqHTfJPCRDaEbid/drOhgitgYku/letMUqOXFoWV0zIIUbjpdH2t+tYj4bQMRQ==}
+ engines: {node: '>=8'}
+
strip-json-comments@3.1.1:
resolution: {integrity: sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==}
engines: {node: '>=8'}
@@ -5392,6 +5693,13 @@ packages:
resolution: {integrity: sha512-b19dMThMV4HVFynSAM1++gBHAbk2Tc/osgLIBZMKsyqh34jb2e8Os7T6ZW/Bt3pJFdBTd2JwAnAAEQV7rSNvcQ==}
engines: {node: '>=0.10'}
+ symbol-tree@3.2.4:
+ resolution: {integrity: sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==}
+
+ tagged-tag@1.0.0:
+ resolution: {integrity: sha512-yEFYrVhod+hdNyx7g5Bnkkb0G6si8HJurOoOEgC8B/O0uXLHlaey/65KRv6cuWBNhBgHKAROVpc7QyYqE5gFng==}
+ engines: {node: '>=20'}
+
tailwind-merge@3.5.0:
resolution: {integrity: sha512-I8K9wewnVDkL1NTGoqWmVEIlUcB9gFriAEkXkfCjX5ib8ezGxtR3xD7iZIxrfArjEsH7F1CHD4RFUtxefdqV/A==}
@@ -5473,6 +5781,13 @@ packages:
resolution: {integrity: sha512-Bf+ILmBgretUrdJxzXM0SgXLZ3XfiaUuOj/IKQHuTXip+05Xn+uyEYdVg0kYDipTBcLrCVyUzAPz7QmArb0mmw==}
engines: {node: '>=14.0.0'}
+ tldts-core@7.0.28:
+ resolution: {integrity: sha512-7W5Efjhsc3chVdFhqtaU0KtK32J37Zcr9RKtID54nG+tIpcY79CQK/veYPODxtD/LJ4Lue66jvrQzIX2Z2/pUQ==}
+
+ tldts@7.0.28:
+ resolution: {integrity: sha512-+Zg3vWhRUv8B1maGSTFdev9mjoo8Etn2Ayfs4cnjlD3CsGkxXX4QyW3j2WJ0wdjYcYmy7Lx2RDsZMhgCWafKIw==}
+ hasBin: true
+
to-regex-range@5.0.1:
resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==}
engines: {node: '>=8.0'}
@@ -5485,9 +5800,17 @@ packages:
resolution: {integrity: sha512-dRXchy+C0IgK8WPC6xvCHFRIWYUbqqdEIKPaKo/AcTUNzwLTK6AH7RjdLWsEZcAN/TBdtfUw3PYEgPr5VPr6ww==}
engines: {node: '>=14.16'}
+ tough-cookie@6.0.1:
+ resolution: {integrity: sha512-LktZQb3IeoUWB9lqR5EWTHgW/VTITCXg4D21M+lvybRVdylLrRMnqaIONLVb5mav8vM19m44HIcGq4qASeu2Qw==}
+ 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'}
+
ts-api-utils@2.5.0:
resolution: {integrity: sha512-OJ/ibxhPlqrMM0UiNHJ/0CKQkoKF243/AEmplt3qpRgkW8VG7IfOS41h7V8TjITqdByHzrjcS/2si+y4lIh8NA==}
engines: {node: '>=18.12'}
@@ -5525,6 +5848,10 @@ packages:
resolution: {integrity: sha512-Ne2YiiGN8bmrmJJEuTWTLJR32nh/JdL1+PSicowtNb0WFpn59GK8/lfD61bVtzguz7b3PBt74nxpv/Pw5po5Rg==}
engines: {node: '>=8'}
+ type-fest@5.5.0:
+ resolution: {integrity: sha512-PlBfpQwiUvGViBNX84Yxwjsdhd1TUlXr6zjX7eoirtCPIr08NAmxwa+fcYBTeRQxHo9YC9wwF3m9i700sHma8g==}
+ engines: {node: '>=20'}
+
type-is@1.6.18:
resolution: {integrity: sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==}
engines: {node: '>= 0.6'}
@@ -5578,6 +5905,10 @@ packages:
undici-types@7.18.2:
resolution: {integrity: sha512-AsuCzffGHJybSaRrmr5eHr81mwJU3kjw6M+uprWvCXiNeN9SOGwQ3Jn8jb8m3Z6izVgknn1R0FTCEAP2QrLY/w==}
+ undici@7.24.7:
+ resolution: {integrity: sha512-H/nlJ/h0ggGC+uRL3ovD+G0i4bqhvsDOpbDv7At5eFLlj2b41L8QliGbnl2H7SnDiYhENphh1tQFJZf+MyfLsQ==}
+ engines: {node: '>=20.18.1'}
+
universalify@2.0.1:
resolution: {integrity: sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==}
engines: {node: '>= 10.0.0'}
@@ -5589,6 +5920,9 @@ packages:
unrs-resolver@1.11.1:
resolution: {integrity: sha512-bSjt9pjaEBnNiGgc9rUiHGKv5l4/TGzDmYw3RhnkJGtLhbnnA/5qJj7x3dNDCRx/PJxu774LlH8lCOlB4hEfKg==}
+ until-async@3.0.2:
+ resolution: {integrity: sha512-IiSk4HlzAMqTUseHHe3VhIGyuFmN90zMTpD3Z3y8jeQbzLIq500MVM7Jq2vUAnTKAFPJrqwkzr6PoTcPhGcOiw==}
+
update-browserslist-db@1.2.3:
resolution: {integrity: sha512-Js0m9cx+qOgDxo0eMiFGEueWztz+d4+M3rGlmKPT+T4IS/jP4ylw3Nwpu6cpTTP8R1MAC1kF4VbdLt3ARf209w==}
hasBin: true
@@ -5722,6 +6056,10 @@ packages:
jsdom:
optional: true
+ w3c-xmlserializer@5.0.0:
+ resolution: {integrity: sha512-o8qghlI8NZHU1lLPrpi2+Uq7abh4GGPpYANlalzWxyWteJOCsr/P+oPBA49TOLu5FTZO4d3F9MnWJfiMo4BkmA==}
+ engines: {node: '>=18'}
+
watchpack@2.5.1:
resolution: {integrity: sha512-Zn5uXdcFNIA1+1Ei5McRd+iRzfhENPCe7LeABkJtNulSxjma+l7ltNx55BWZkRlwRnpOgHqxnjyaDgJnNXnqzg==}
engines: {node: '>=10.13.0'}
@@ -5741,6 +6079,10 @@ packages:
webidl-conversions@3.0.1:
resolution: {integrity: sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==}
+ webidl-conversions@8.0.1:
+ resolution: {integrity: sha512-BMhLD/Sw+GbJC21C/UgyaZX41nPt8bUTg+jWyDeg7e7YN4xOM05YPSIXceACnXVtqyEw/LMClUQMtMZ+PGGpqQ==}
+ engines: {node: '>=20'}
+
webpack-node-externals@3.0.0:
resolution: {integrity: sha512-LnL6Z3GGDPht/AigwRh2dvL9PQPFQ8skEpVrWZXLWBYmqcaojHNN0onvHzie6rq7EWKrrBfPYqNEzTJgiwEQDQ==}
engines: {node: '>=6'}
@@ -5767,6 +6109,14 @@ packages:
resolution: {integrity: sha512-OqedPIGOfsDlo31UNwYbCFMSaO9m9G/0faIHj5/dZFDMFqPTcx6UwqyOy3COEaEOg/9VsGIpdqn62W5KhoKSpg==}
engines: {node: '>=0.8.0'}
+ whatwg-mimetype@5.0.0:
+ resolution: {integrity: sha512-sXcNcHOC51uPGF0P/D4NVtrkjSU2fNsm9iog4ZvZJsL3rjoDAzXZhkm2MWt1y+PUdggKAYVoMAIYcs78wJ51Cw==}
+ engines: {node: '>=20'}
+
+ whatwg-url@16.0.1:
+ resolution: {integrity: sha512-1to4zXBxmXHV3IiSSEInrreIlu02vUOvrhxJJH5vcxYTBDAx51cqZiKdyTxlecdKNSjj8EcxGBxNf6Vg+945gw==}
+ engines: {node: ^20.19.0 || ^22.12.0 || >=24.0.0}
+
whatwg-url@5.0.0:
resolution: {integrity: sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==}
@@ -5802,6 +6152,13 @@ packages:
wrappy@1.0.2:
resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==}
+ 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==}
+
xtend@4.0.2:
resolution: {integrity: sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==}
engines: {node: '>=0.4'}
@@ -5871,6 +6228,8 @@ packages:
snapshots:
+ '@adobe/css-tools@4.4.4': {}
+
'@alloc/quick-lru@5.2.0': {}
'@angular-devkit/core@19.2.23(chokidar@4.0.3)':
@@ -5906,6 +6265,22 @@ snapshots:
transitivePeerDependencies:
- chokidar
+ '@asamuzakjp/css-color@5.1.8':
+ dependencies:
+ '@csstools/css-calc': 3.1.1(@csstools/css-parser-algorithms@4.0.0(@csstools/css-tokenizer@4.0.0))(@csstools/css-tokenizer@4.0.0)
+ '@csstools/css-color-parser': 4.0.2(@csstools/css-parser-algorithms@4.0.0(@csstools/css-tokenizer@4.0.0))(@csstools/css-tokenizer@4.0.0)
+ '@csstools/css-parser-algorithms': 4.0.0(@csstools/css-tokenizer@4.0.0)
+ '@csstools/css-tokenizer': 4.0.0
+
+ '@asamuzakjp/dom-selector@7.0.8':
+ dependencies:
+ '@asamuzakjp/nwsapi': 2.3.9
+ bidi-js: 1.0.3
+ css-tree: 3.2.1
+ is-potential-custom-element-name: 1.0.1
+
+ '@asamuzakjp/nwsapi@2.3.9': {}
+
'@aws-crypto/crc32@5.2.0':
dependencies:
'@aws-crypto/util': 5.2.0
@@ -6430,6 +6805,8 @@ snapshots:
transitivePeerDependencies:
- supports-color
+ '@babel/helper-plugin-utils@7.28.6': {}
+
'@babel/helper-string-parser@7.27.1': {}
'@babel/helper-validator-identifier@7.28.5': {}
@@ -6445,6 +6822,16 @@ snapshots:
dependencies:
'@babel/types': 7.29.0
+ '@babel/plugin-transform-react-jsx-self@7.27.1(@babel/core@7.29.0)':
+ dependencies:
+ '@babel/core': 7.29.0
+ '@babel/helper-plugin-utils': 7.28.6
+
+ '@babel/plugin-transform-react-jsx-source@7.27.1(@babel/core@7.29.0)':
+ dependencies:
+ '@babel/core': 7.29.0
+ '@babel/helper-plugin-utils': 7.28.6
+
'@babel/runtime@7.29.2': {}
'@babel/template@7.28.6':
@@ -6472,9 +6859,37 @@ snapshots:
'@borewit/text-codec@0.2.2': {}
+ '@bramus/specificity@2.4.2':
+ dependencies:
+ css-tree: 3.2.1
+
'@colors/colors@1.5.0':
optional: true
+ '@csstools/color-helpers@6.0.2': {}
+
+ '@csstools/css-calc@3.1.1(@csstools/css-parser-algorithms@4.0.0(@csstools/css-tokenizer@4.0.0))(@csstools/css-tokenizer@4.0.0)':
+ dependencies:
+ '@csstools/css-parser-algorithms': 4.0.0(@csstools/css-tokenizer@4.0.0)
+ '@csstools/css-tokenizer': 4.0.0
+
+ '@csstools/css-color-parser@4.0.2(@csstools/css-parser-algorithms@4.0.0(@csstools/css-tokenizer@4.0.0))(@csstools/css-tokenizer@4.0.0)':
+ dependencies:
+ '@csstools/color-helpers': 6.0.2
+ '@csstools/css-calc': 3.1.1(@csstools/css-parser-algorithms@4.0.0(@csstools/css-tokenizer@4.0.0))(@csstools/css-tokenizer@4.0.0)
+ '@csstools/css-parser-algorithms': 4.0.0(@csstools/css-tokenizer@4.0.0)
+ '@csstools/css-tokenizer': 4.0.0
+
+ '@csstools/css-parser-algorithms@4.0.0(@csstools/css-tokenizer@4.0.0)':
+ dependencies:
+ '@csstools/css-tokenizer': 4.0.0
+
+ '@csstools/css-syntax-patches-for-csstree@1.1.2(css-tree@3.2.1)':
+ optionalDependencies:
+ css-tree: 3.2.1
+
+ '@csstools/css-tokenizer@4.0.0': {}
+
'@electric-sql/pglite-socket@0.1.1(@electric-sql/pglite@0.4.1)':
dependencies:
'@electric-sql/pglite': 0.4.1
@@ -6625,6 +7040,10 @@ snapshots:
'@eslint/core': 0.17.0
levn: 0.4.1
+ '@exodus/bytes@1.15.0(@noble/hashes@2.0.1)':
+ optionalDependencies:
+ '@noble/hashes': 2.0.1
+
'@fastify/busboy@3.2.0': {}
'@fastify/otel@0.18.0(@opentelemetry/api@1.9.1)':
@@ -6985,6 +7404,15 @@ snapshots:
transitivePeerDependencies:
- supports-color
+ '@mswjs/interceptors@0.41.3':
+ dependencies:
+ '@open-draft/deferred-promise': 2.2.0
+ '@open-draft/logger': 0.3.0
+ '@open-draft/until': 2.1.0
+ is-node-process: 1.2.0
+ outvariant: 1.4.3
+ strict-event-emitter: 0.5.1
+
'@napi-rs/wasm-runtime@0.2.12':
dependencies:
'@emnapi/core': 1.9.2
@@ -7191,6 +7619,15 @@ snapshots:
dependencies:
consola: 3.4.2
+ '@open-draft/deferred-promise@2.2.0': {}
+
+ '@open-draft/logger@0.3.0':
+ dependencies:
+ is-node-process: 1.2.0
+ outvariant: 1.4.3
+
+ '@open-draft/until@2.1.0': {}
+
'@opentelemetry/api-logs@0.207.0':
dependencies:
'@opentelemetry/api': 1.9.1
@@ -7687,6 +8124,8 @@ snapshots:
react: 18.3.1
react-redux: 9.2.0(@types/react@18.3.28)(react@18.3.1)(redux@5.0.1)
+ '@rolldown/pluginutils@1.0.0-beta.27': {}
+
'@rollup/plugin-commonjs@28.0.1(rollup@4.60.1)':
dependencies:
'@rollup/pluginutils': 5.3.0(rollup@4.60.1)
@@ -8355,6 +8794,40 @@ snapshots:
'@swc/counter': 0.1.3
tslib: 2.8.1
+ '@testing-library/dom@10.4.1':
+ dependencies:
+ '@babel/code-frame': 7.29.0
+ '@babel/runtime': 7.29.2
+ '@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.2(@testing-library/dom@10.4.1)(@types/react-dom@18.3.7(@types/react@18.3.28))(@types/react@18.3.28)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)':
+ dependencies:
+ '@babel/runtime': 7.29.2
+ '@testing-library/dom': 10.4.1
+ react: 18.3.1
+ react-dom: 18.3.1(react@18.3.1)
+ optionalDependencies:
+ '@types/react': 18.3.28
+ '@types/react-dom': 18.3.7(@types/react@18.3.28)
+
+ '@testing-library/user-event@14.6.1(@testing-library/dom@10.4.1)':
+ dependencies:
+ '@testing-library/dom': 10.4.1
+
'@tokenizer/inflate@0.4.1':
dependencies:
debug: 4.4.3
@@ -8390,6 +8863,29 @@ snapshots:
tslib: 2.8.1
optional: true
+ '@types/aria-query@5.0.4': {}
+
+ '@types/babel__core@7.20.5':
+ dependencies:
+ '@babel/parser': 7.29.2
+ '@babel/types': 7.29.0
+ '@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.29.0
+
+ '@types/babel__template@7.4.4':
+ dependencies:
+ '@babel/parser': 7.29.2
+ '@babel/types': 7.29.0
+
+ '@types/babel__traverse@7.28.0':
+ dependencies:
+ '@babel/types': 7.29.0
+
'@types/bcrypt@6.0.0':
dependencies:
'@types/node': 25.5.2
@@ -8596,6 +9092,8 @@ snapshots:
'@types/http-errors': 2.0.5
'@types/node': 25.5.2
+ '@types/statuses@2.0.6': {}
+
'@types/superagent@8.1.9':
dependencies:
'@types/cookiejar': 2.1.5
@@ -8773,6 +9271,18 @@ snapshots:
'@unrs/resolver-binding-win32-x64-msvc@1.11.1':
optional: true
+ '@vitejs/plugin-react@4.7.0(vite@7.3.2(@types/node@25.5.2)(jiti@1.21.7)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3))':
+ dependencies:
+ '@babel/core': 7.29.0
+ '@babel/plugin-transform-react-jsx-self': 7.27.1(@babel/core@7.29.0)
+ '@babel/plugin-transform-react-jsx-source': 7.27.1(@babel/core@7.29.0)
+ '@rolldown/pluginutils': 1.0.0-beta.27
+ '@types/babel__core': 7.20.5
+ react-refresh: 0.17.0
+ vite: 7.3.2(@types/node@25.5.2)(jiti@1.21.7)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3)
+ transitivePeerDependencies:
+ - supports-color
+
'@vitest/expect@4.1.3':
dependencies:
'@standard-schema/spec': 1.1.0
@@ -8782,20 +9292,22 @@ snapshots:
chai: 6.2.2
tinyrainbow: 3.1.0
- '@vitest/mocker@4.1.3(vite@7.3.2(@types/node@25.5.2)(jiti@1.21.7)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3))':
+ '@vitest/mocker@4.1.3(msw@2.13.2(@types/node@25.5.2)(typescript@6.0.2))(vite@7.3.2(@types/node@25.5.2)(jiti@1.21.7)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3))':
dependencies:
'@vitest/spy': 4.1.3
estree-walker: 3.0.3
magic-string: 0.30.21
optionalDependencies:
+ msw: 2.13.2(@types/node@25.5.2)(typescript@6.0.2)
vite: 7.3.2(@types/node@25.5.2)(jiti@1.21.7)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3)
- '@vitest/mocker@4.1.3(vite@7.3.2(@types/node@25.5.2)(jiti@2.6.1)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3))':
+ '@vitest/mocker@4.1.3(msw@2.13.2(@types/node@25.5.2)(typescript@6.0.2))(vite@7.3.2(@types/node@25.5.2)(jiti@2.6.1)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3))':
dependencies:
'@vitest/spy': 4.1.3
estree-walker: 3.0.3
magic-string: 0.30.21
optionalDependencies:
+ msw: 2.13.2(@types/node@25.5.2)(typescript@6.0.2)
vite: 7.3.2(@types/node@25.5.2)(jiti@2.6.1)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3)
'@vitest/pretty-format@4.1.3':
@@ -8994,6 +9506,8 @@ snapshots:
dependencies:
color-convert: 2.0.1
+ ansi-styles@5.2.0: {}
+
ansi-styles@6.2.3: {}
ansis@4.2.0: {}
@@ -9011,6 +9525,12 @@ snapshots:
argparse@2.0.1: {}
+ aria-query@5.3.0:
+ dependencies:
+ dequal: 2.0.3
+
+ aria-query@5.3.2: {}
+
array-timsort@1.0.3: {}
arrify@2.0.1:
@@ -9065,6 +9585,10 @@ snapshots:
better-result@2.8.1: {}
+ bidi-js@1.0.3:
+ dependencies:
+ require-from-string: 2.0.2
+
bignumber.js@9.3.1: {}
binary-extensions@2.3.0: {}
@@ -9243,7 +9767,6 @@ snapshots:
string-width: 4.2.3
strip-ansi: 6.0.1
wrap-ansi: 7.0.0
- optional: true
clone@1.0.4: {}
@@ -9310,6 +9833,8 @@ snapshots:
cookie@0.7.2: {}
+ cookie@1.1.1: {}
+
cookiejar@2.1.4: {}
cors@2.8.6:
@@ -9332,6 +9857,13 @@ snapshots:
shebang-command: 2.0.0
which: 2.0.2
+ css-tree@3.2.1:
+ dependencies:
+ mdn-data: 2.27.1
+ source-map-js: 1.2.1
+
+ css.escape@1.5.1: {}
+
csscolorparser@1.0.3: {}
cssesc@3.0.0: {}
@@ -9378,6 +9910,13 @@ snapshots:
data-uri-to-buffer@4.0.1: {}
+ data-urls@7.0.0(@noble/hashes@2.0.1):
+ dependencies:
+ whatwg-mimetype: 5.0.0
+ whatwg-url: 16.0.1(@noble/hashes@2.0.1)
+ transitivePeerDependencies:
+ - '@noble/hashes'
+
dateformat@4.6.3: {}
debug@4.4.3:
@@ -9386,6 +9925,8 @@ snapshots:
decimal.js-light@2.5.1: {}
+ decimal.js@10.6.0: {}
+
deep-is@0.1.4: {}
deepmerge-ts@7.1.5: {}
@@ -9425,6 +9966,8 @@ snapshots:
tsconfig-paths-webpack-plugin: 4.2.0
watskeburt: 5.0.3
+ dequal@2.0.3: {}
+
destr@2.0.5: {}
detect-libc@2.1.2: {}
@@ -9438,6 +9981,10 @@ snapshots:
dlv@1.1.3: {}
+ dom-accessibility-api@0.5.16: {}
+
+ dom-accessibility-api@0.6.3: {}
+
dom-serializer@2.0.0:
dependencies:
domelementtype: 2.3.0
@@ -9508,6 +10055,8 @@ snapshots:
entities@4.5.0: {}
+ entities@6.0.1: {}
+
entities@7.0.1: {}
env-paths@3.0.0: {}
@@ -10008,8 +10557,7 @@ snapshots:
geojson-vt@4.0.2: {}
- get-caller-file@2.0.5:
- optional: true
+ get-caller-file@2.0.5: {}
get-east-asian-width@1.5.0: {}
@@ -10128,6 +10676,8 @@ snapshots:
graphmatch@1.1.1: {}
+ graphql@16.13.2: {}
+
grid-index@1.1.0: {}
gtoken@7.1.0:
@@ -10160,12 +10710,20 @@ snapshots:
dependencies:
function-bind: 1.1.2
+ headers-polyfill@4.0.3: {}
+
helmet@8.1.0: {}
help-me@5.0.0: {}
hono@4.12.12: {}
+ html-encoding-sniffer@6.0.0(@noble/hashes@2.0.1):
+ dependencies:
+ '@exodus/bytes': 1.15.0(@noble/hashes@2.0.1)
+ transitivePeerDependencies:
+ - '@noble/hashes'
+
html-entities@2.6.0:
optional: true
@@ -10248,6 +10806,8 @@ snapshots:
imurmurhash@0.1.4: {}
+ indent-string@4.0.0: {}
+
inherits@2.0.4: {}
ini@4.1.1: {}
@@ -10307,12 +10867,16 @@ snapshots:
is-interactive@1.0.0: {}
+ is-node-process@1.2.0: {}
+
is-number@7.0.0: {}
is-path-inside@4.0.0: {}
is-plain-object@5.0.0: {}
+ is-potential-custom-element-name@1.0.1: {}
+
is-promise@4.0.0: {}
is-property@1.0.2: {}
@@ -10352,6 +10916,32 @@ snapshots:
dependencies:
argparse: 2.0.1
+ jsdom@29.0.2(@noble/hashes@2.0.1):
+ dependencies:
+ '@asamuzakjp/css-color': 5.1.8
+ '@asamuzakjp/dom-selector': 7.0.8
+ '@bramus/specificity': 2.4.2
+ '@csstools/css-syntax-patches-for-csstree': 1.1.2(css-tree@3.2.1)
+ '@exodus/bytes': 1.15.0(@noble/hashes@2.0.1)
+ css-tree: 3.2.1
+ data-urls: 7.0.0(@noble/hashes@2.0.1)
+ decimal.js: 10.6.0
+ html-encoding-sniffer: 6.0.0(@noble/hashes@2.0.1)
+ is-potential-custom-element-name: 1.0.1
+ lru-cache: 11.3.2
+ parse5: 8.0.0
+ saxes: 6.0.0
+ symbol-tree: 3.2.4
+ tough-cookie: 6.0.1
+ undici: 7.24.7
+ w3c-xmlserializer: 5.0.0
+ webidl-conversions: 8.0.1
+ whatwg-mimetype: 5.0.0
+ whatwg-url: 16.0.1(@noble/hashes@2.0.1)
+ xml-name-validator: 5.0.0
+ transitivePeerDependencies:
+ - '@noble/hashes'
+
jsesc@3.1.0: {}
json-bigint@1.0.0:
@@ -10532,6 +11122,8 @@ snapshots:
dependencies:
react: 18.3.1
+ lz-string@1.5.0: {}
+
magic-string@0.30.17:
dependencies:
'@jridgewell/sourcemap-codec': 1.5.5
@@ -10575,6 +11167,8 @@ snapshots:
math-intrinsics@1.1.0: {}
+ mdn-data@2.27.1: {}
+
media-typer@0.3.0: {}
media-typer@1.1.0: {}
@@ -10617,6 +11211,8 @@ snapshots:
mimic-function@5.0.1: {}
+ min-indent@1.0.1: {}
+
minimatch@10.2.5:
dependencies:
brace-expansion: 5.0.5
@@ -10633,6 +11229,31 @@ snapshots:
ms@2.1.3: {}
+ msw@2.13.2(@types/node@25.5.2)(typescript@6.0.2):
+ dependencies:
+ '@inquirer/confirm': 5.1.21(@types/node@25.5.2)
+ '@mswjs/interceptors': 0.41.3
+ '@open-draft/deferred-promise': 2.2.0
+ '@types/statuses': 2.0.6
+ cookie: 1.1.1
+ graphql: 16.13.2
+ headers-polyfill: 4.0.3
+ is-node-process: 1.2.0
+ outvariant: 1.4.3
+ path-to-regexp: 6.3.0
+ picocolors: 1.1.1
+ rettime: 0.10.1
+ statuses: 2.0.2
+ strict-event-emitter: 0.5.1
+ tough-cookie: 6.0.1
+ type-fest: 5.5.0
+ until-async: 3.0.2
+ yargs: 17.7.2
+ optionalDependencies:
+ typescript: 6.0.2
+ transitivePeerDependencies:
+ - '@types/node'
+
multer@2.1.1:
dependencies:
append-field: 1.0.0
@@ -10796,6 +11417,8 @@ snapshots:
strip-ansi: 6.0.1
wcwidth: 1.0.1
+ outvariant@1.4.3: {}
+
p-limit@3.1.0:
dependencies:
yocto-queue: 0.1.0
@@ -10817,6 +11440,10 @@ snapshots:
parse-srcset@1.0.2: {}
+ parse5@8.0.0:
+ dependencies:
+ entities: 6.0.1
+
parseurl@1.3.3: {}
passport-google-oauth20@2.0.0:
@@ -10861,6 +11488,8 @@ snapshots:
lru-cache: 11.3.2
minipass: 7.1.3
+ path-to-regexp@6.3.0: {}
+
path-to-regexp@8.3.0: {}
path-to-regexp@8.4.2: {}
@@ -11047,6 +11676,12 @@ snapshots:
prettier@3.8.1: {}
+ pretty-format@27.5.1:
+ dependencies:
+ ansi-regex: 5.0.1
+ ansi-styles: 5.2.0
+ react-is: 17.0.2
+
prisma@7.7.0(@types/react-dom@18.3.7(@types/react@18.3.28))(@types/react@18.3.28)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@6.0.2):
dependencies:
'@prisma/config': 7.7.0
@@ -11157,6 +11792,8 @@ snapshots:
dependencies:
react: 18.3.1
+ react-is@17.0.2: {}
+
react-is@19.2.4: {}
react-redux@9.2.0(@types/react@18.3.28)(react@18.3.1)(redux@5.0.1):
@@ -11168,6 +11805,8 @@ snapshots:
'@types/react': 18.3.28
redux: 5.0.1
+ react-refresh@0.17.0: {}
+
react@18.3.1:
dependencies:
loose-envify: 1.4.0
@@ -11214,6 +11853,11 @@ snapshots:
dependencies:
resolve: 1.22.11
+ redent@3.0.0:
+ dependencies:
+ indent-string: 4.0.0
+ strip-indent: 3.0.0
+
redis-errors@1.2.0: {}
redis-parser@3.0.0:
@@ -11232,8 +11876,7 @@ snapshots:
remeda@2.33.4: {}
- require-directory@2.1.1:
- optional: true
+ require-directory@2.1.1: {}
require-from-string@2.0.2: {}
@@ -11285,6 +11928,8 @@ snapshots:
retry@0.13.1:
optional: true
+ rettime@0.10.1: {}
+
reusify@1.1.0: {}
rfdc@1.4.1: {}
@@ -11363,6 +12008,10 @@ snapshots:
parse-srcset: 1.0.2
postcss: 8.5.8
+ saxes@6.0.0:
+ dependencies:
+ xmlchars: 2.2.0
+
scheduler@0.23.2:
dependencies:
loose-envify: 1.4.0
@@ -11514,6 +12163,8 @@ snapshots:
streamsearch@1.1.0: {}
+ strict-event-emitter@0.5.1: {}
+
string-argv@0.3.2: {}
string-width@4.2.3:
@@ -11547,6 +12198,10 @@ snapshots:
strip-bom@3.0.0: {}
+ strip-indent@3.0.0:
+ dependencies:
+ min-indent: 1.0.1
+
strip-json-comments@3.1.1: {}
strip-json-comments@5.0.3: {}
@@ -11628,6 +12283,10 @@ snapshots:
symbol-observable@4.0.0: {}
+ symbol-tree@3.2.4: {}
+
+ tagged-tag@1.0.0: {}
+
tailwind-merge@3.5.0: {}
tailwindcss-animate@1.0.7(tailwindcss@3.4.19(tsx@4.21.0)(yaml@2.8.3)):
@@ -11727,6 +12386,12 @@ snapshots:
tinyrainbow@3.1.0: {}
+ tldts-core@7.0.28: {}
+
+ tldts@7.0.28:
+ dependencies:
+ tldts-core: 7.0.28
+
to-regex-range@5.0.1:
dependencies:
is-number: 7.0.0
@@ -11739,8 +12404,16 @@ snapshots:
'@tokenizer/token': 0.3.0
ieee754: 1.2.1
+ tough-cookie@6.0.1:
+ dependencies:
+ tldts: 7.0.28
+
tr46@0.0.3: {}
+ tr46@6.0.0:
+ dependencies:
+ punycode: 2.3.1
+
ts-api-utils@2.5.0(typescript@6.0.2):
dependencies:
typescript: 6.0.2
@@ -11784,6 +12457,10 @@ snapshots:
type-fest@0.7.1: {}
+ type-fest@5.5.0:
+ dependencies:
+ tagged-tag: 1.0.0
+
type-is@1.6.18:
dependencies:
media-typer: 0.3.0
@@ -11834,6 +12511,8 @@ snapshots:
undici-types@7.18.2: {}
+ undici@7.24.7: {}
+
universalify@2.0.1: {}
unpipe@1.0.0: {}
@@ -11862,6 +12541,8 @@ snapshots:
'@unrs/resolver-binding-win32-ia32-msvc': 1.11.1
'@unrs/resolver-binding-win32-x64-msvc': 1.11.1
+ until-async@3.0.2: {}
+
update-browserslist-db@1.2.3(browserslist@4.28.2):
dependencies:
browserslist: 4.28.2
@@ -11944,10 +12625,10 @@ snapshots:
tsx: 4.21.0
yaml: 2.8.3
- vitest@4.1.3(@opentelemetry/api@1.9.1)(@types/node@25.5.2)(vite@7.3.2(@types/node@25.5.2)(jiti@1.21.7)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3)):
+ vitest@4.1.3(@opentelemetry/api@1.9.1)(@types/node@25.5.2)(jsdom@29.0.2(@noble/hashes@2.0.1))(msw@2.13.2(@types/node@25.5.2)(typescript@6.0.2))(vite@7.3.2(@types/node@25.5.2)(jiti@1.21.7)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3)):
dependencies:
'@vitest/expect': 4.1.3
- '@vitest/mocker': 4.1.3(vite@7.3.2(@types/node@25.5.2)(jiti@1.21.7)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3))
+ '@vitest/mocker': 4.1.3(msw@2.13.2(@types/node@25.5.2)(typescript@6.0.2))(vite@7.3.2(@types/node@25.5.2)(jiti@1.21.7)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3))
'@vitest/pretty-format': 4.1.3
'@vitest/runner': 4.1.3
'@vitest/snapshot': 4.1.3
@@ -11969,13 +12650,14 @@ snapshots:
optionalDependencies:
'@opentelemetry/api': 1.9.1
'@types/node': 25.5.2
+ jsdom: 29.0.2(@noble/hashes@2.0.1)
transitivePeerDependencies:
- msw
- vitest@4.1.3(@opentelemetry/api@1.9.1)(@types/node@25.5.2)(vite@7.3.2(@types/node@25.5.2)(jiti@2.6.1)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3)):
+ vitest@4.1.3(@opentelemetry/api@1.9.1)(@types/node@25.5.2)(jsdom@29.0.2(@noble/hashes@2.0.1))(msw@2.13.2(@types/node@25.5.2)(typescript@6.0.2))(vite@7.3.2(@types/node@25.5.2)(jiti@2.6.1)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3)):
dependencies:
'@vitest/expect': 4.1.3
- '@vitest/mocker': 4.1.3(vite@7.3.2(@types/node@25.5.2)(jiti@2.6.1)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3))
+ '@vitest/mocker': 4.1.3(msw@2.13.2(@types/node@25.5.2)(typescript@6.0.2))(vite@7.3.2(@types/node@25.5.2)(jiti@2.6.1)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3))
'@vitest/pretty-format': 4.1.3
'@vitest/runner': 4.1.3
'@vitest/snapshot': 4.1.3
@@ -11997,9 +12679,14 @@ snapshots:
optionalDependencies:
'@opentelemetry/api': 1.9.1
'@types/node': 25.5.2
+ jsdom: 29.0.2(@noble/hashes@2.0.1)
transitivePeerDependencies:
- msw
+ w3c-xmlserializer@5.0.0:
+ dependencies:
+ xml-name-validator: 5.0.0
+
watchpack@2.5.1:
dependencies:
glob-to-regexp: 0.4.1
@@ -12015,6 +12702,8 @@ snapshots:
webidl-conversions@3.0.1: {}
+ webidl-conversions@8.0.1: {}
+
webpack-node-externals@3.0.0: {}
webpack-sources@3.3.4: {}
@@ -12059,6 +12748,16 @@ snapshots:
websocket-extensions@0.1.4: {}
+ whatwg-mimetype@5.0.0: {}
+
+ whatwg-url@16.0.1(@noble/hashes@2.0.1):
+ dependencies:
+ '@exodus/bytes': 1.15.0(@noble/hashes@2.0.1)
+ tr46: 6.0.0
+ webidl-conversions: 8.0.1
+ transitivePeerDependencies:
+ - '@noble/hashes'
+
whatwg-url@5.0.0:
dependencies:
tr46: 0.0.3
@@ -12088,7 +12787,6 @@ snapshots:
ansi-styles: 4.3.0
string-width: 4.2.3
strip-ansi: 6.0.1
- optional: true
wrap-ansi@9.0.2:
dependencies:
@@ -12098,10 +12796,13 @@ snapshots:
wrappy@1.0.2: {}
+ xml-name-validator@5.0.0: {}
+
+ xmlchars@2.2.0: {}
+
xtend@4.0.2: {}
- y18n@5.0.8:
- optional: true
+ y18n@5.0.8: {}
yallist@3.1.1: {}
@@ -12120,7 +12821,6 @@ snapshots:
string-width: 4.2.3
y18n: 5.0.8
yargs-parser: 21.1.1
- optional: true
yocto-queue@0.1.0: {}