diff --git a/app/javascript/entrypoints/s3_browser.tsx b/app/javascript/entrypoints/s3_browser.tsx index 548d47f..7f53eaf 100644 --- a/app/javascript/entrypoints/s3_browser.tsx +++ b/app/javascript/entrypoints/s3_browser.tsx @@ -1,5 +1,5 @@ import { createRoot } from 'react-dom/client'; -import App from '../../s3browser/src/app/App'; +import App from '../src/app/App'; const s3BrowserAppElement = document.getElementById('s3-browser-app'); if (!s3BrowserAppElement) throw new Error('S3 Browser app root element not found'); diff --git a/app/s3browser/src/app/App.tsx b/app/javascript/src/app/App.tsx similarity index 100% rename from app/s3browser/src/app/App.tsx rename to app/javascript/src/app/App.tsx diff --git a/app/s3browser/src/app/provider.tsx b/app/javascript/src/app/provider.tsx similarity index 100% rename from app/s3browser/src/app/provider.tsx rename to app/javascript/src/app/provider.tsx diff --git a/app/s3browser/src/app/router.tsx b/app/javascript/src/app/router.tsx similarity index 100% rename from app/s3browser/src/app/router.tsx rename to app/javascript/src/app/router.tsx diff --git a/app/javascript/src/app/routes/__tests__/bucket-contents.test.tsx b/app/javascript/src/app/routes/__tests__/bucket-contents.test.tsx new file mode 100644 index 0000000..64698a2 --- /dev/null +++ b/app/javascript/src/app/routes/__tests__/bucket-contents.test.tsx @@ -0,0 +1,208 @@ +import { describe, it, expect } from 'vitest'; +import { + buildBucketContents, + buildS3Object, + mockApi, + renderApp, + screen, + within, + waitForElementToBeRemoved, + userEvent, +} from '@/testing/test-utils'; +import BucketContentsTable from '@/features/file-browser/components/bucket-contents-table'; + +const renderAppAndWait = async () => { + await renderApp(, { + url: '/browse/buckets/my-bucket', + path: '/browse/buckets/:bucketName', + }); + await waitForElementToBeRemoved(() => screen.queryByRole('status')); +}; + +describe('BucketContentsRoute', () => { + describe('rendering bucket contents', () => { + it('renders folders and objects together, with folders typed as "Folder"', async () => { + const testBucket = buildBucketContents({ + folders: ['documents/'], + objects: [buildS3Object({ key: 'notes.txt' })], + }); + mockApi('get', '/buckets/my-bucket/list', testBucket); + + await renderAppAndWait(); + + expect(screen.getByText('documents')).toBeInTheDocument(); + expect(screen.getByText('notes.txt')).toBeInTheDocument(); + expect(screen.getByText('Folder')).toBeInTheDocument(); + expect(screen.getByText('txt')).toBeInTheDocument(); + }); + + it('displays the expected column headers', async () => { + mockApi('get', '/buckets/my-bucket/list', buildBucketContents()); + + await renderAppAndWait(); + + const headers = await screen.findAllByRole('columnheader'); + expect(headers.map((header) => header.textContent?.trim())).toEqual([ + 'Name', + 'Last Modified', + 'Type', + 'Storage Class', + 'Size', + ]); + }); + + it('shows the bucket name as the current directory at the root', async () => { + mockApi('get', '/buckets/my-bucket/list', buildBucketContents()); + + await renderAppAndWait(); + + expect(await screen.findByText('my-bucket/')).toBeInTheDocument(); + }); + + it('shows the last prefix segment as the current directory inside a folder', async () => { + mockApi('get', '/buckets/my-bucket/list', buildBucketContents()); + + await renderAppAndWait(); + + expect(await screen.findByText('my-bucket/')).toBeInTheDocument(); + }); + + it('renders the empty state when the bucket has no contents', async () => { + mockApi('get', '/buckets/my-bucket/list', buildBucketContents()); + + await renderAppAndWait(); + + expect(screen.getByText('No entries found.')).toBeInTheDocument(); + }); + + it('formats object size and renders "-" for folder size/storage class', async () => { + mockApi( + 'get', + '/buckets/my-bucket/list', + buildBucketContents({ + folders: ['archive/'], + objects: [ + buildS3Object({ + key: 'big.bin', + size: 1024 * 1024, + storageClass: 'INTELLIGENT_TIERING', + }), + ], + }), + ); + + await renderAppAndWait(); + + const objectRow = screen.getByText('big.bin').closest('tr')!; + expect(within(objectRow).getByText('1.00 MB')).toBeInTheDocument(); + expect(within(objectRow).getByText('Intelligent Tiering')).toBeInTheDocument(); + + // Folders have no Last Modified, Storage Class or Size so they all render "-" + const folderRow = screen.getByText('archive').closest('tr')!; + expect(within(folderRow).getAllByText('-')).toHaveLength(3); + }); + }); + + describe('sorting bucket contents', () => { + const getNameOrder = () => { + const rows = screen.getAllByRole('row').slice(1); // drop header row + return rows.map((row) => within(row).getAllByRole('cell')[0].textContent); + }; + + it('reverses the name order when the Name header is clicked', async () => { + mockApi( + 'get', + '/buckets/my-bucket/list', + buildBucketContents({ + folders: ['mango/'], + objects: [buildS3Object({ key: 'apple.txt' }), buildS3Object({ key: 'zebra.txt' })], + }), + ); + + await renderAppAndWait(); + + // Name column is initially sorted ascending, the first click reverses the order + await userEvent.click(screen.getByRole('button', { name: 'Name' })); + expect(getNameOrder()).toEqual(['zebra.txt', 'mango', 'apple.txt']); + }); + + it('sorts folders before objects by type, then by extension, then by name', async () => { + mockApi( + 'get', + '/buckets/my-bucket/list', + buildBucketContents({ + folders: ['z-folder/', 'a-folder/'], + objects: [ + buildS3Object({ key: 'delta.txt' }), + buildS3Object({ key: 'beta.txt' }), + buildS3Object({ key: 'zoo.jpg' }), + ], + }), + ); + + await renderAppAndWait(); + await userEvent.click(screen.getByRole('button', { name: 'Type' })); + + // Folders first (alphabetical), then objects by extension (jpg < txt), + // then by name within the same extension (beta < delta). + expect(getNameOrder()).toEqual(['a-folder', 'z-folder', 'zoo.jpg', 'beta.txt', 'delta.txt']); + }); + + it('sorts objects chronologically by last modified, folders last', async () => { + mockApi( + 'get', + '/buckets/my-bucket/list', + buildBucketContents({ + folders: ['folder/'], + objects: [ + buildS3Object({ key: 'newer.txt', lastModified: '2026-03-01T00:00:00.000Z' }), + buildS3Object({ key: 'older.txt', lastModified: '2026-01-01T00:00:00.000Z' }), + ], + }), + ); + + await renderAppAndWait(); + await userEvent.click(screen.getByRole('button', { name: 'Last Modified' })); + + expect(getNameOrder()).toEqual(['older.txt', 'newer.txt', 'folder']); + }); + + it('sorts objects by storage class, folders last', async () => { + mockApi( + 'get', + '/buckets/my-bucket/list', + buildBucketContents({ + folders: ['folder/'], + objects: [ + buildS3Object({ key: 'archival.txt', storageClass: 'INTELLIGENT_TIERING' }), + buildS3Object({ key: 'standard.txt', storageClass: 'STANDARD' }), + ], + }), + ); + + await renderAppAndWait(); + await userEvent.click(screen.getByRole('button', { name: 'Storage Class' })); + + expect(getNameOrder()).toEqual(['archival.txt', 'standard.txt', 'folder']); + }); + + it('sorts objects numerically by size, folders last', async () => { + mockApi( + 'get', + '/buckets/my-bucket/list', + buildBucketContents({ + folders: ['folder/'], + objects: [ + buildS3Object({ key: 'big.txt', size: 1024 * 1024 }), + buildS3Object({ key: 'small.txt', size: 10 }), + ], + }), + ); + + await renderAppAndWait(); + await userEvent.click(screen.getByRole('button', { name: 'Size' })); + + expect(getNameOrder()).toEqual(['small.txt', 'big.txt', 'folder']); + }); + }); +}); diff --git a/app/javascript/src/app/routes/__tests__/buckets.test.tsx b/app/javascript/src/app/routes/__tests__/buckets.test.tsx new file mode 100644 index 0000000..c6f89b3 --- /dev/null +++ b/app/javascript/src/app/routes/__tests__/buckets.test.tsx @@ -0,0 +1,49 @@ +import { describe, it, expect } from 'vitest'; +import { buildBucket, mockApi, renderApp, screen, within } from '@/testing/test-utils'; +import BucketList from '@/features/file-browser/components/bucket-list'; + +describe('BucketsRoute', () => { + it('renders a list of buckets', async () => { + const bucket1 = buildBucket({ name: 'bucket-1' }); + const bucket2 = buildBucket({ name: 'bucket-2' }); + + mockApi('get', '/buckets', { buckets: [bucket1, bucket2] }); + + await renderApp(, { url: '/buckets' }); + + expect(await screen.findByRole('heading', { name: 'S3 Buckets' })).toBeInTheDocument(); + expect(await screen.findByText('bucket-1')).toBeInTheDocument(); + expect(await screen.findByText('bucket-2')).toBeInTheDocument(); + }); + + it('should display correct column headers', async () => { + const bucket1 = buildBucket({ name: 'bucket-1' }); + const bucket2 = buildBucket({ name: 'bucket-2' }); + + mockApi('get', '/buckets', { buckets: [bucket1, bucket2] }); + + await renderApp(, { url: '/buckets' }); + + const columnHeaders = await screen.findAllByRole('columnheader'); + const columnHeaderNames = columnHeaders.map((header) => header.textContent); + + expect(columnHeaderNames).toEqual(['Name', 'Description']); + }); + + it('should display data sorted in ascending order by name', async () => { + const bucket1 = buildBucket({ name: 'z-bucket' }); + const bucket2 = buildBucket({ name: 'a-bucket' }); + const bucket3 = buildBucket({ name: 'm-bucket' }); + + mockApi('get', '/buckets', { buckets: [bucket1, bucket2, bucket3] }); + + await renderApp(, { url: '/buckets' }); + + await screen.findByRole('table'); + + const rows = screen.getAllByRole('row'); + const names = rows.slice(1).map((row) => within(row).getAllByRole('cell')[0].textContent); + + expect(names).toEqual(['a-bucket', 'm-bucket', 'z-bucket']); + }); +}); diff --git a/app/javascript/src/app/routes/__tests__/object-details.test.tsx b/app/javascript/src/app/routes/__tests__/object-details.test.tsx new file mode 100644 index 0000000..350cba8 --- /dev/null +++ b/app/javascript/src/app/routes/__tests__/object-details.test.tsx @@ -0,0 +1,149 @@ +import { describe, it, expect } from 'vitest'; +import { + buildObjectDetails, + mockApi, + renderApp, + screen, + within, + waitFor, +} from '@/testing/test-utils'; +import ObjectDetailsRoute from '@/app/routes/object-details'; + +const renderRoute = (objectKey: string, bucketName = 'test-bucket') => + renderApp(, { + url: `/browse/buckets/${bucketName}/object-details?prefix=${encodeURIComponent(objectKey)}`, + path: '/browse/buckets/:bucketName/object-details', + }); + +// MSW matches on pathname and ignores the query string, +// so we register the endpoint without the "?key=" part" +const mockObjectDetails = ( + details: ReturnType, + bucketName = 'test-bucket', +) => mockApi('get', `/buckets/${bucketName}/object`, details); + +// Each field renders as
wrapping label
and value
+const fieldByLabel = (label: string) => { + const wrapper = screen.getByText(label).closest('div'); + if (!wrapper) throw new Error(`Could not find field wrapper for label: ${label}`); + return within(wrapper); +}; + +describe('ObjectDetailsRoute', () => { + it('fetches and renders the object information', async () => { + mockObjectDetails( + buildObjectDetails({ + key: 'documents/report.pdf', + size: 2060, + storageClass: 'STANDARD', + lastModified: '2026-01-15T10:30:00.000Z', + }), + ); + + await renderRoute('documents/report.pdf'); + + expect(await screen.findByRole('heading', { name: 'report.pdf' })).toBeInTheDocument(); + + expect(fieldByLabel('Key').getByText('documents/report.pdf')).toBeInTheDocument(); + expect(fieldByLabel('Type').getByText('pdf')).toBeInTheDocument(); + expect(fieldByLabel('Size').getByText('2.01 kB')).toBeInTheDocument(); + expect(fieldByLabel('Last modified').getByText(/January 15, 2026/)).toBeInTheDocument(); + }); + + it('sets the document title from the object key', async () => { + mockObjectDetails(buildObjectDetails({ key: 'documents/report.pdf' })); + + await renderRoute('documents/report.pdf'); + + await screen.findByRole('heading', { name: 'report.pdf' }); + await waitFor(() => expect(document.title).toBe('Object Details - documents/report.pdf')); + }); + + it('shows "unknown" type when the key has no file extension', async () => { + mockObjectDetails(buildObjectDetails({ key: 'README' })); + + await renderRoute('README'); + + expect(await screen.findByRole('heading', { name: 'README' })).toBeInTheDocument(); + expect(fieldByLabel('Type').getByText('unknown')).toBeInTheDocument(); + }); + + it('omits the archive-specific fields for STANDARD storage class', async () => { + mockObjectDetails(buildObjectDetails({ storageClass: 'STANDARD' })); + + await renderRoute('documents/report.pdf'); + + expect(await screen.findByText('Standard')).toBeInTheDocument(); + expect(screen.queryByText('Access tier')).not.toBeInTheDocument(); + expect(screen.queryByText('Retrieval time')).not.toBeInTheDocument(); + expect(screen.queryByText('Restoration status')).not.toBeInTheDocument(); + }); + + it('renders Archive Access tier with a 5 hour retrieval time', async () => { + mockObjectDetails( + buildObjectDetails({ + storageClass: 'INTELLIGENT_TIERING', + archiveStatus: 'ARCHIVE_ACCESS', + restoreStatus: null, + }), + ); + + await renderRoute('documents/report.pdf'); + + await screen.findByText('Intelligent Tiering'); + expect(fieldByLabel('Access tier').getByText('Archive Access')).toBeInTheDocument(); + expect(fieldByLabel('Retrieval time').getByText('5 hours')).toBeInTheDocument(); + expect( + fieldByLabel('Restoration status').getByText('No restoration currently in progress'), + ).toBeInTheDocument(); + }); + + it('renders Deep Archive Access tier with a 12 hour retrieval time', async () => { + mockObjectDetails( + buildObjectDetails({ + storageClass: 'INTELLIGENT_TIERING', + archiveStatus: 'DEEP_ARCHIVE_ACCESS', + }), + ); + + await renderRoute('documents/report.pdf'); + + await screen.findByText('Intelligent Tiering'); + expect(fieldByLabel('Access tier').getByText('Deep Archive Access')).toBeInTheDocument(); + expect(fieldByLabel('Retrieval time').getByText('12 hours')).toBeInTheDocument(); + }); + + it('shows instant-access for a non-standard object without an archive status', async () => { + mockObjectDetails( + buildObjectDetails({ + storageClass: 'INTELLIGENT_TIERING', + archiveStatus: null, + }), + ); + + await renderRoute('documents/report.pdf'); + + await screen.findByText('Intelligent Tiering'); + expect( + fieldByLabel('Access tier').getByText(/Frequent Access, Infrequent Access/), + ).toBeInTheDocument(); + expect( + fieldByLabel('Retrieval time').getByText('Milliseconds (instant access)'), + ).toBeInTheDocument(); + }); + + it('displays in-progress restoration status when the object is being restored', async () => { + mockObjectDetails( + buildObjectDetails({ + storageClass: 'INTELLIGENT_TIERING', + archiveStatus: 'ARCHIVE_ACCESS', + restoreStatus: 'IN_PROGRESS', + }), + ); + + await renderRoute('documents/report.pdf'); + + await screen.findByText('Intelligent Tiering'); + expect(fieldByLabel('Restoration status').getByText('In Progress')).toBeInTheDocument(); + }); +}); diff --git a/app/s3browser/src/app/routes/bucket-contents.tsx b/app/javascript/src/app/routes/bucket-contents.tsx similarity index 100% rename from app/s3browser/src/app/routes/bucket-contents.tsx rename to app/javascript/src/app/routes/bucket-contents.tsx diff --git a/app/s3browser/src/app/routes/buckets.tsx b/app/javascript/src/app/routes/buckets.tsx similarity index 100% rename from app/s3browser/src/app/routes/buckets.tsx rename to app/javascript/src/app/routes/buckets.tsx diff --git a/app/s3browser/src/app/routes/not-found.tsx b/app/javascript/src/app/routes/not-found.tsx similarity index 100% rename from app/s3browser/src/app/routes/not-found.tsx rename to app/javascript/src/app/routes/not-found.tsx diff --git a/app/s3browser/src/app/routes/object-details.tsx b/app/javascript/src/app/routes/object-details.tsx similarity index 64% rename from app/s3browser/src/app/routes/object-details.tsx rename to app/javascript/src/app/routes/object-details.tsx index f47b655..a9d13e1 100644 --- a/app/s3browser/src/app/routes/object-details.tsx +++ b/app/javascript/src/app/routes/object-details.tsx @@ -1,9 +1,6 @@ -import { LoaderFunctionArgs, useParams, useSearchParams } from 'react-router'; +import { LoaderFunctionArgs, useSearchParams } from 'react-router'; import { QueryClient } from '@tanstack/react-query'; -import { - getObjectDetailsQueryOptions, - useObjectDetailsSuspenseQuery, -} from '@/features/file-browser/api/get-object-details'; +import { getObjectDetailsQueryOptions } from '@/features/file-browser/api/get-object-details'; import ObjectDetailDisplay from '@/features/file-browser/components/object-detail-display'; export const clientLoader = @@ -18,16 +15,13 @@ export const clientLoader = }; const ObjectDetailsRoute = () => { - const params = useParams(); - const bucketName = params.bucketName as string; const [searchParams] = useSearchParams(); const key = searchParams.get('prefix') ?? ''; - const query = useObjectDetailsSuspenseQuery({ bucket: bucketName, key }); return ( <> {`Object Details - ${key}`} - + ); }; diff --git a/app/s3browser/src/components/errors/main.tsx b/app/javascript/src/components/errors/main.tsx similarity index 100% rename from app/s3browser/src/components/errors/main.tsx rename to app/javascript/src/components/errors/main.tsx diff --git a/app/s3browser/src/components/errors/route-error.tsx b/app/javascript/src/components/errors/route-error.tsx similarity index 100% rename from app/s3browser/src/components/errors/route-error.tsx rename to app/javascript/src/components/errors/route-error.tsx diff --git a/app/s3browser/src/components/layouts/main-layout.tsx b/app/javascript/src/components/layouts/main-layout.tsx similarity index 100% rename from app/s3browser/src/components/layouts/main-layout.tsx rename to app/javascript/src/components/layouts/main-layout.tsx diff --git a/app/s3browser/src/components/ui/notifications/notification.tsx b/app/javascript/src/components/ui/notifications/notification.tsx similarity index 100% rename from app/s3browser/src/components/ui/notifications/notification.tsx rename to app/javascript/src/components/ui/notifications/notification.tsx diff --git a/app/s3browser/src/components/ui/notifications/notifications.tsx b/app/javascript/src/components/ui/notifications/notifications.tsx similarity index 100% rename from app/s3browser/src/components/ui/notifications/notifications.tsx rename to app/javascript/src/components/ui/notifications/notifications.tsx diff --git a/app/s3browser/src/components/ui/table-builder/table-body.tsx b/app/javascript/src/components/ui/table-builder/table-body.tsx similarity index 100% rename from app/s3browser/src/components/ui/table-builder/table-body.tsx rename to app/javascript/src/components/ui/table-builder/table-body.tsx diff --git a/app/s3browser/src/components/ui/table-builder/table-builder.tsx b/app/javascript/src/components/ui/table-builder/table-builder.tsx similarity index 100% rename from app/s3browser/src/components/ui/table-builder/table-builder.tsx rename to app/javascript/src/components/ui/table-builder/table-builder.tsx diff --git a/app/s3browser/src/components/ui/table-builder/table-header.tsx b/app/javascript/src/components/ui/table-builder/table-header.tsx similarity index 100% rename from app/s3browser/src/components/ui/table-builder/table-header.tsx rename to app/javascript/src/components/ui/table-builder/table-header.tsx diff --git a/app/s3browser/src/components/ui/table-builder/table-pagination.tsx b/app/javascript/src/components/ui/table-builder/table-pagination.tsx similarity index 100% rename from app/s3browser/src/components/ui/table-builder/table-pagination.tsx rename to app/javascript/src/components/ui/table-builder/table-pagination.tsx diff --git a/app/s3browser/src/components/ui/table-builder/table-row.tsx b/app/javascript/src/components/ui/table-builder/table-row.tsx similarity index 100% rename from app/s3browser/src/components/ui/table-builder/table-row.tsx rename to app/javascript/src/components/ui/table-builder/table-row.tsx diff --git a/app/s3browser/src/features/file-browser/api/get-bucket-contents.ts b/app/javascript/src/features/file-browser/api/get-bucket-contents.ts similarity index 100% rename from app/s3browser/src/features/file-browser/api/get-bucket-contents.ts rename to app/javascript/src/features/file-browser/api/get-bucket-contents.ts diff --git a/app/s3browser/src/features/file-browser/api/get-buckets.ts b/app/javascript/src/features/file-browser/api/get-buckets.ts similarity index 100% rename from app/s3browser/src/features/file-browser/api/get-buckets.ts rename to app/javascript/src/features/file-browser/api/get-buckets.ts diff --git a/app/s3browser/src/features/file-browser/api/get-object-details.ts b/app/javascript/src/features/file-browser/api/get-object-details.ts similarity index 100% rename from app/s3browser/src/features/file-browser/api/get-object-details.ts rename to app/javascript/src/features/file-browser/api/get-object-details.ts diff --git a/app/s3browser/src/features/file-browser/components/breadcrumbs.tsx b/app/javascript/src/features/file-browser/components/breadcrumbs.tsx similarity index 100% rename from app/s3browser/src/features/file-browser/components/breadcrumbs.tsx rename to app/javascript/src/features/file-browser/components/breadcrumbs.tsx diff --git a/app/s3browser/src/features/file-browser/components/bucket-contents-table.tsx b/app/javascript/src/features/file-browser/components/bucket-contents-table.tsx similarity index 100% rename from app/s3browser/src/features/file-browser/components/bucket-contents-table.tsx rename to app/javascript/src/features/file-browser/components/bucket-contents-table.tsx diff --git a/app/s3browser/src/features/file-browser/components/bucket-list.tsx b/app/javascript/src/features/file-browser/components/bucket-list.tsx similarity index 100% rename from app/s3browser/src/features/file-browser/components/bucket-list.tsx rename to app/javascript/src/features/file-browser/components/bucket-list.tsx diff --git a/app/s3browser/src/features/file-browser/components/object-detail-display.tsx b/app/javascript/src/features/file-browser/components/object-detail-display.tsx similarity index 78% rename from app/s3browser/src/features/file-browser/components/object-detail-display.tsx rename to app/javascript/src/features/file-browser/components/object-detail-display.tsx index 8aa7145..7ef06a9 100644 --- a/app/s3browser/src/features/file-browser/components/object-detail-display.tsx +++ b/app/javascript/src/features/file-browser/components/object-detail-display.tsx @@ -1,5 +1,5 @@ import { useMemo } from 'react'; -import { ObjectDetails } from '@/types/api'; +import { useParams, useSearchParams } from 'react-router'; import { formatSize, formatLastModified, @@ -7,6 +7,7 @@ import { extractName, extractFileExtension, } from '../utils/format-utils'; +import { useObjectDetailsSuspenseQuery } from '../api/get-object-details'; import ObjectDetailField from './object-detail-field'; const displayRetrievalTime = (archiveStatus: string | null) => { @@ -25,15 +26,19 @@ const displayAccessTierLabel = (archiveStatus: string | null) => ? capitalizeStr(archiveStatus) : 'Frequent Access, Infrequent Access, or Archive Instant Access tier'; -type ObjectDetailDisplayProps = { - objectDetails: ObjectDetails; -}; +const ObjectDetailDisplay = () => { + const params = useParams(); + const bucketName = params.bucketName as string; + const [searchParams] = useSearchParams(); + const key = searchParams.get('prefix') ?? ''; + + const query = useObjectDetailsSuspenseQuery({ bucket: bucketName, key }); + const objectDetails = query.data; -const ObjectDetailDisplay = ({ objectDetails }: ObjectDetailDisplayProps) => { - const { key, size, lastModified, storageClass, archiveStatus, restoreStatus } = objectDetails; + const { size, lastModified, storageClass, archiveStatus, restoreStatus } = objectDetails; const isNonStandard = storageClass !== 'STANDARD'; - const fileName = useMemo(() => extractName(key), [key]); + const fileName = useMemo(() => extractName(objectDetails.key), [objectDetails.key]); return (
diff --git a/app/s3browser/src/features/file-browser/components/object-detail-field.tsx b/app/javascript/src/features/file-browser/components/object-detail-field.tsx similarity index 100% rename from app/s3browser/src/features/file-browser/components/object-detail-field.tsx rename to app/javascript/src/features/file-browser/components/object-detail-field.tsx diff --git a/app/s3browser/src/features/file-browser/hooks/use-pagination.tsx b/app/javascript/src/features/file-browser/hooks/use-pagination.tsx similarity index 100% rename from app/s3browser/src/features/file-browser/hooks/use-pagination.tsx rename to app/javascript/src/features/file-browser/hooks/use-pagination.tsx diff --git a/app/s3browser/src/features/file-browser/utils/bucket-contents-column-defs.tsx b/app/javascript/src/features/file-browser/utils/bucket-contents-column-defs.tsx similarity index 100% rename from app/s3browser/src/features/file-browser/utils/bucket-contents-column-defs.tsx rename to app/javascript/src/features/file-browser/utils/bucket-contents-column-defs.tsx diff --git a/app/s3browser/src/features/file-browser/utils/bucket-list-column-defs.tsx b/app/javascript/src/features/file-browser/utils/bucket-list-column-defs.tsx similarity index 100% rename from app/s3browser/src/features/file-browser/utils/bucket-list-column-defs.tsx rename to app/javascript/src/features/file-browser/utils/bucket-list-column-defs.tsx diff --git a/app/s3browser/src/features/file-browser/utils/format-utils.ts b/app/javascript/src/features/file-browser/utils/format-utils.ts similarity index 100% rename from app/s3browser/src/features/file-browser/utils/format-utils.ts rename to app/javascript/src/features/file-browser/utils/format-utils.ts diff --git a/app/s3browser/src/features/file-browser/utils/transform-to-bucket-items.ts b/app/javascript/src/features/file-browser/utils/transform-to-bucket-items.ts similarity index 100% rename from app/s3browser/src/features/file-browser/utils/transform-to-bucket-items.ts rename to app/javascript/src/features/file-browser/utils/transform-to-bucket-items.ts diff --git a/app/s3browser/src/lib/api-client.ts b/app/javascript/src/lib/api-client.ts similarity index 100% rename from app/s3browser/src/lib/api-client.ts rename to app/javascript/src/lib/api-client.ts diff --git a/app/s3browser/src/lib/api-error.ts b/app/javascript/src/lib/api-error.ts similarity index 100% rename from app/s3browser/src/lib/api-error.ts rename to app/javascript/src/lib/api-error.ts diff --git a/app/s3browser/src/lib/auth.ts b/app/javascript/src/lib/auth.ts similarity index 100% rename from app/s3browser/src/lib/auth.ts rename to app/javascript/src/lib/auth.ts diff --git a/app/s3browser/src/lib/react-query.ts b/app/javascript/src/lib/react-query.ts similarity index 100% rename from app/s3browser/src/lib/react-query.ts rename to app/javascript/src/lib/react-query.ts diff --git a/app/s3browser/src/stores/notifications-store.ts b/app/javascript/src/stores/notifications-store.ts similarity index 100% rename from app/s3browser/src/stores/notifications-store.ts rename to app/javascript/src/stores/notifications-store.ts diff --git a/app/javascript/src/testing/data-generators.ts b/app/javascript/src/testing/data-generators.ts new file mode 100644 index 0000000..6c34c83 --- /dev/null +++ b/app/javascript/src/testing/data-generators.ts @@ -0,0 +1,48 @@ +import type { Bucket, S3Object, BucketContentsResponse, ObjectDetails } from '@/types/api'; + +const BUCKET_DEFAULTS: Bucket = { + name: 'test-bucket', + description: 'A test bucket for unit tests', +}; + +export const buildBucket = (overrides?: Partial): Bucket => ({ + ...BUCKET_DEFAULTS, + ...overrides, +}); + +const S3_OBJECT_DEFAULTS: S3Object = { + key: 'test-object.txt', + size: 1024, + lastModified: '2026-01-15T10:30:00.000Z', + storageClass: 'STANDARD', +}; + +export const buildS3Object = (overrides?: Partial): S3Object => ({ + ...S3_OBJECT_DEFAULTS, + ...overrides, +}); + +// Build a full bucket-contents API response +export const buildBucketContents = ( + overrides?: Partial, +): BucketContentsResponse => ({ + objects: [], + folders: [], + ...overrides, +}); + +const OBJECT_DETAILS_DEFAULTS: ObjectDetails = { + key: 'documents/report.pdf', + bucket: 'test-bucket', + size: 2048, + contentType: 'application/pdf', + lastModified: '2026-01-15T10:30:00.000Z', + storageClass: 'STANDARD', + archiveStatus: null, + restoreStatus: null, +}; + +export const buildObjectDetails = (overrides?: Partial): ObjectDetails => ({ + ...OBJECT_DETAILS_DEFAULTS, + ...overrides, +}); diff --git a/app/javascript/src/testing/mock-api.ts b/app/javascript/src/testing/mock-api.ts new file mode 100644 index 0000000..69d7531 --- /dev/null +++ b/app/javascript/src/testing/mock-api.ts @@ -0,0 +1,12 @@ +import { http, HttpResponse, JsonBodyType } from 'msw'; +import { setupServer } from 'msw/node'; + +export const server = setupServer(); + +type HttpMethod = 'get'; + +// Register a mock API response for a single endpoint. Call once per endpoint per test. +// Overrides are cleared automatically by `server.resetHandlers()` in the global `afterEach`. +export const mockApi = (method: HttpMethod, path: string, body: JsonBodyType, status = 200) => { + server.use(http[method](`api${path}`, () => HttpResponse.json(body, { status }))); +}; diff --git a/app/javascript/src/testing/setup.ts b/app/javascript/src/testing/setup.ts new file mode 100644 index 0000000..275377f --- /dev/null +++ b/app/javascript/src/testing/setup.ts @@ -0,0 +1,15 @@ +import { beforeAll, afterEach, afterAll } from 'vitest'; +import '@testing-library/jest-dom/vitest'; +import { server } from './mock-api'; + +beforeAll(() => { + server.listen({ onUnhandledRequest: 'warn' }); +}); + +afterEach(() => { + server.resetHandlers(); +}); + +afterAll(() => { + server.close(); +}); diff --git a/app/javascript/src/testing/test-utils.tsx b/app/javascript/src/testing/test-utils.tsx new file mode 100644 index 0000000..be6da6d --- /dev/null +++ b/app/javascript/src/testing/test-utils.tsx @@ -0,0 +1,68 @@ +import React from 'react'; +import { + render as rtlRender, + screen, + waitForElementToBeRemoved, + waitFor, + within, +} from '@testing-library/react'; +import { QueryClient, QueryClientProvider } from '@tanstack/react-query'; +import { RouterProvider, createMemoryRouter } from 'react-router'; +import { Notifications } from '@/components/ui/notifications/notifications'; + +export { + buildBucket, + buildObjectDetails, + buildS3Object, + buildBucketContents, +} from './data-generators'; +export { mockApi } from './mock-api'; + +const createTestQueryClient = () => + new QueryClient({ + defaultOptions: { + queries: { + retry: false, + gcTime: Infinity, + }, + }, + }); + +/* + renderApp - renders a component inside a QueryClient + MemoryRouter +*/ + +interface RenderAppOptions { + url?: string; + path?: string; + [key: string]: unknown; +} + +export const renderApp = async ( + ui: React.ReactElement, + { + url = '/', // simulated browser location to navigate to (e.g. '/users/janedoe/edit') + path, // route pattern React Router uses for matching and resolving params (e.g. '/users/:userUid/edit') + ...renderOptions + }: RenderAppOptions = {}, +) => { + const queryClient = createTestQueryClient(); + const routePath = path ?? url; // defaults to url — only pass path for parameterized routes + + const router = createMemoryRouter([{ path: routePath, element: ui }], { + initialEntries: url ? ['/', url] : ['/'], + initialIndex: url ? 1 : 0, + }); + + return rtlRender( + + + + , + renderOptions, + ); +}; + +export * from '@testing-library/react'; +export { default as userEvent } from '@testing-library/user-event'; +export { screen, waitForElementToBeRemoved, waitFor, within }; diff --git a/app/s3browser/src/types/api.ts b/app/javascript/src/types/api.ts similarity index 100% rename from app/s3browser/src/types/api.ts rename to app/javascript/src/types/api.ts diff --git a/config/vite.json b/config/vite.json index 22f4a6d..7ec601d 100644 --- a/config/vite.json +++ b/config/vite.json @@ -13,4 +13,4 @@ "publicOutputDir": "vite-test", "port": 3037 } -} +} \ No newline at end of file diff --git a/lib/tasks/atc/ci.rake b/lib/tasks/atc/ci.rake index d9ed46f..10deb81 100644 --- a/lib/tasks/atc/ci.rake +++ b/lib/tasks/atc/ci.rake @@ -20,7 +20,7 @@ namespace :atc do task ci_nocop: ['atc:setup:config_files', :environment, 'atc:ci_specs'] desc 'CI build with Rubocop validation' - task ci: ['atc:setup:config_files', :environment, 'atc:rubocop', 'atc:eslint', 'atc:prettier', 'atc:ci_specs'] + task ci: ['atc:setup:config_files', :environment, 'atc:rubocop', 'atc:eslint', 'atc:prettier', 'atc:vitest', 'atc:ci_specs'] desc 'CI build just running specs' task ci_specs: :environment do @@ -67,6 +67,15 @@ namespace :atc do end end + desc 'Run frontend tests' + task :vitest do + success = system('yarn test --run') + unless success + puts 'Frontend tests failed.' + exit 1 + end + end + rescue LoadError => e # Be prepared to rescue so that this rake file can exist in environments where RSpec is unavailable (i.e. production environments). puts '[Warning] Exception creating ci/rubocop/rspec rake tasks. '\ diff --git a/package.json b/package.json index cc1a04f..7c271e1 100644 --- a/package.json +++ b/package.json @@ -5,10 +5,11 @@ "type": "module", "scripts": { "start:dev": "./bin/vite dev", - "lint": "eslint app/s3browser app/javascript/entrypoints", - "lint:fix": "eslint app/s3browser app/javascript/entrypoints --fix", - "format": "prettier --write app/s3browser app/javascript/entrypoints", - "format:check": "prettier --check app/s3browser app/javascript/entrypoints" + "test": "vitest", + "lint": "eslint app/javascript", + "lint:fix": "eslint app/javascript --fix", + "format": "prettier --write app/javascript", + "format:check": "prettier --check app/javascript" }, "packageManager": "yarn@4.14.1", "dependencies": { @@ -32,6 +33,10 @@ "devDependencies": { "@eslint/js": "^9.39.2", "@faker-js/faker": "^10.4.0", + "@testing-library/dom": "^10.4.1", + "@testing-library/jest-dom": "^6.9.1", + "@testing-library/react": "^16.3.2", + "@testing-library/user-event": "^14.6.1", "@types/bootstrap": "^5", "@types/faker": "^6", "@types/node": "^25.6.0", @@ -43,13 +48,16 @@ "eslint-plugin-react": "^7.37.5", "eslint-plugin-react-hooks": "^7.0.1", "globals": "^17.6.0", + "jsdom": "^29.1.1", + "msw": "^2.14.6", "prettier": "3.8.3", "typescript": "^5.9.3", "typescript-eslint": "^8.53.1", "vite": "^8.0.0", - "vite-plugin-ruby": "^5.2.0" + "vite-plugin-ruby": "^5.2.0", + "vitest": "^4.1.8" }, "browserslist": [ "defaults" ] -} \ No newline at end of file +} diff --git a/tsconfig.json b/tsconfig.json index d35cc26..0745dec 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -31,12 +31,12 @@ "baseUrl": ".", "paths": { "@/*": [ - "./app/s3browser/src/*" + "app/javascript/src/*" ] } }, "include": [ "./app/javascript/entrypoints", - "./app/s3browser/src" + "app/javascript/src" ], } \ No newline at end of file diff --git a/vite.config.mts b/vite.config.mts index b9fb633..37b8d0a 100644 --- a/vite.config.mts +++ b/vite.config.mts @@ -1,16 +1,19 @@ +/// import { defineConfig } from 'vite'; import path from 'path'; import RubyPlugin from 'vite-plugin-ruby'; import ReactPlugin from '@vitejs/plugin-react'; export default defineConfig({ - plugins: [ - RubyPlugin(), - ReactPlugin(), - ], + plugins: [RubyPlugin(), ReactPlugin()], resolve: { alias: { - '@': path.resolve(__dirname, './app/s3browser/src') - } - } -}) + '@': path.resolve(__dirname, './app/javascript/src'), + }, + }, + test: { + globals: true, + environment: 'jsdom', + setupFiles: './src/testing/setup.ts', + }, +}); diff --git a/yarn.lock b/yarn.lock index 603bb59..8bc2a5e 100644 --- a/yarn.lock +++ b/yarn.lock @@ -5,6 +5,64 @@ __metadata: version: 9 cacheKey: 10c0 +"@adobe/css-tools@npm:^4.4.0": + version: 4.5.0 + resolution: "@adobe/css-tools@npm:4.5.0" + checksum: 10c0/fc969e1117098eb4cccdb73beb2508daa0e52760af1183d6288bafea59204943490ab3ede28593032ffb8929c0cee270b2a53254fe61139ab00604ea8fc33cea + languageName: node + linkType: hard + +"@asamuzakjp/css-color@npm:^5.1.11": + version: 5.1.11 + resolution: "@asamuzakjp/css-color@npm:5.1.11" + dependencies: + "@asamuzakjp/generational-cache": "npm:^1.0.1" + "@csstools/css-calc": "npm:^3.2.0" + "@csstools/css-color-parser": "npm:^4.1.0" + "@csstools/css-parser-algorithms": "npm:^4.0.0" + "@csstools/css-tokenizer": "npm:^4.0.0" + checksum: 10c0/32720bdff8daea6a8847aba6cdfae55baa3b4a2690b51d21db7f0382bbd183f3d9f2d5126df50afd889062635684b2819e47113629ee2e80c99389e75f48d060 + languageName: node + linkType: hard + +"@asamuzakjp/dom-selector@npm:^7.1.1": + version: 7.1.1 + resolution: "@asamuzakjp/dom-selector@npm:7.1.1" + dependencies: + "@asamuzakjp/generational-cache": "npm:^1.0.1" + "@asamuzakjp/nwsapi": "npm:^2.3.9" + bidi-js: "npm:^1.0.3" + css-tree: "npm:^3.2.1" + is-potential-custom-element-name: "npm:^1.0.1" + checksum: 10c0/8cec1c618781c94de5836a215bbe5aafb4d8b835b18c51faf8547f4574afa39f92def3951e40123860062467613dd825f1e1600ff32e8045cc099a91796dcfb8 + languageName: node + linkType: hard + +"@asamuzakjp/generational-cache@npm:^1.0.1": + version: 1.0.1 + resolution: "@asamuzakjp/generational-cache@npm:1.0.1" + checksum: 10c0/1de62de43764e13fca3b9a31b7ea9b1bf0780fe053d266e40378a19ff8c66b543e011e6a0df02d410cd59bf981126706f176cdbb938985165202c4a079fe1057 + languageName: node + linkType: hard + +"@asamuzakjp/nwsapi@npm:^2.3.9": + version: 2.3.9 + resolution: "@asamuzakjp/nwsapi@npm:2.3.9" + checksum: 10c0/869b81382e775499c96c45c6dbe0d0766a6da04bcf0abb79f5333535c4e19946851acaa43398f896e2ecc5a1de9cf3db7cf8c4b1afac1ee3d15e21584546d74d + languageName: node + linkType: hard + +"@babel/code-frame@npm:^7.10.4": + version: 7.29.7 + resolution: "@babel/code-frame@npm:7.29.7" + dependencies: + "@babel/helper-validator-identifier": "npm:^7.29.7" + js-tokens: "npm:^4.0.0" + picocolors: "npm:^1.1.1" + checksum: 10c0/169fc2080169a40c1760155eaaaf739bcb882df0bec76a83adbda5493645bc17270a3434b8848c494b1933e96fe1d147370001e3cda09a39f43ae30f08ef2069 + languageName: node + linkType: hard + "@babel/code-frame@npm:^7.28.6, @babel/code-frame@npm:^7.29.0": version: 7.29.0 resolution: "@babel/code-frame@npm:7.29.0" @@ -116,6 +174,13 @@ __metadata: languageName: node linkType: hard +"@babel/helper-validator-identifier@npm:^7.29.7": + version: 7.29.7 + resolution: "@babel/helper-validator-identifier@npm:7.29.7" + checksum: 10c0/4795354e7ae0dcafa72de1cd04ec51252dc1498517170beaf019e03effc5b7bf13c6b21a3949a77e07b8125be7f106ed1131350d8ebd4566ae874094a726d62b + languageName: node + linkType: hard + "@babel/helper-validator-option@npm:^7.27.1": version: 7.27.1 resolution: "@babel/helper-validator-option@npm:7.27.1" @@ -144,6 +209,13 @@ __metadata: languageName: node linkType: hard +"@babel/runtime@npm:^7.12.5": + version: 7.29.7 + resolution: "@babel/runtime@npm:7.29.7" + checksum: 10c0/ca11572f7146b21e0bde6a9ed4bb6a89eafbee5f0944c7eb54d0d8a2dac962c33638a1d611e14faa71dfbb92b4b5f9236232208568a6b7d5c6f3f39ddb91771e + languageName: node + linkType: hard + "@babel/runtime@npm:^7.24.7, @babel/runtime@npm:^7.26.0, @babel/runtime@npm:^7.5.5, @babel/runtime@npm:^7.6.3, @babel/runtime@npm:^7.8.7": version: 7.29.2 resolution: "@babel/runtime@npm:7.29.2" @@ -187,6 +259,75 @@ __metadata: languageName: node linkType: hard +"@bramus/specificity@npm:^2.4.2": + version: 2.4.2 + resolution: "@bramus/specificity@npm:2.4.2" + dependencies: + css-tree: "npm:^3.0.0" + bin: + specificity: bin/cli.js + checksum: 10c0/c5f4e04e0bca0d2202598207a5eb0733c8109d12a68a329caa26373bec598d99db5bb785b8865fefa00fc01b08c6068138807ceb11a948fe15e904ed6cf4ba72 + languageName: node + linkType: hard + +"@csstools/color-helpers@npm:^6.0.2": + version: 6.0.2 + resolution: "@csstools/color-helpers@npm:6.0.2" + checksum: 10c0/4c66574563d7c960010c11e41c2673675baff07c427cca6e8dddffa5777de45770d13ff3efce1c0642798089ad55de52870d9d8141f78db3fa5bba012f2d3789 + languageName: node + linkType: hard + +"@csstools/css-calc@npm:^3.2.0, @csstools/css-calc@npm:^3.2.1": + version: 3.2.1 + resolution: "@csstools/css-calc@npm:3.2.1" + peerDependencies: + "@csstools/css-parser-algorithms": ^4.0.0 + "@csstools/css-tokenizer": ^4.0.0 + checksum: 10c0/0191c8d1cd4dffa0d3b6bfd1e78a721934b1d7a6c972966e4fdaa72208c6789e8ff443ee81764a32f1e6107825695b5524ef2b4dc1681b5b29230f2a1277e5df + languageName: node + linkType: hard + +"@csstools/css-color-parser@npm:^4.1.0": + version: 4.1.1 + resolution: "@csstools/css-color-parser@npm:4.1.1" + dependencies: + "@csstools/color-helpers": "npm:^6.0.2" + "@csstools/css-calc": "npm:^3.2.1" + peerDependencies: + "@csstools/css-parser-algorithms": ^4.0.0 + "@csstools/css-tokenizer": ^4.0.0 + checksum: 10c0/427bd32f1a8917342a70a6fd97b93bb492aae7c8790e7782b5d6edc8c08064bb8aef0a86099f286db00288f9afea85eb92c46350e9057f5fea058e03a2a09203 + languageName: node + linkType: hard + +"@csstools/css-parser-algorithms@npm:^4.0.0": + version: 4.0.0 + resolution: "@csstools/css-parser-algorithms@npm:4.0.0" + peerDependencies: + "@csstools/css-tokenizer": ^4.0.0 + checksum: 10c0/94558c2428d6ef0ddef542e86e0a8376aa1263a12a59770abb13ba50d7b83086822c75433f32aa2e7fef00555e1cc88292f9ca5bce79aed232bb3fed73b1528d + languageName: node + linkType: hard + +"@csstools/css-syntax-patches-for-csstree@npm:^1.1.3": + version: 1.1.4 + resolution: "@csstools/css-syntax-patches-for-csstree@npm:1.1.4" + peerDependencies: + css-tree: ^3.2.1 + peerDependenciesMeta: + css-tree: + optional: true + checksum: 10c0/3872a7befb553c53249c87e964ac00f55d059f4574d2cc023e03e1dafc86a5ad19f6a6d05fa2c14fb192e6a4538a73158104cc2e32e0688f27fd841b9ba76568 + languageName: node + linkType: hard + +"@csstools/css-tokenizer@npm:^4.0.0": + version: 4.0.0 + resolution: "@csstools/css-tokenizer@npm:4.0.0" + checksum: 10c0/669cf3d0f9c8e1ffdf8c9955ad8beba0c8cfe03197fe29a4fcbd9ee6f7a18856cfa42c62670021a75183d9ab37f5d14a866e6a9df753a6c07f59e36797a9ea9f + languageName: node + linkType: hard + "@emnapi/core@npm:1.10.0": version: 1.10.0 resolution: "@emnapi/core@npm:1.10.0" @@ -303,6 +444,18 @@ __metadata: languageName: node linkType: hard +"@exodus/bytes@npm:^1.11.0, @exodus/bytes@npm:^1.15.0, @exodus/bytes@npm:^1.6.0": + version: 1.15.1 + resolution: "@exodus/bytes@npm:1.15.1" + peerDependencies: + "@noble/hashes": ^1.8.0 || ^2.0.0 + peerDependenciesMeta: + "@noble/hashes": + optional: true + checksum: 10c0/333056a6953bbf875d9f3b86c32314de29458d842e5f56f6ef8034b18c2d9660184550093d1bae5de0064043d5e23f54cc03148798d9d29cf5167ac03f2e9f8c + languageName: node + linkType: hard + "@faker-js/faker@npm:^10.4.0": version: 10.4.0 resolution: "@faker-js/faker@npm:10.4.0" @@ -386,6 +539,67 @@ __metadata: languageName: node linkType: hard +"@inquirer/ansi@npm:^2.0.7": + version: 2.0.7 + resolution: "@inquirer/ansi@npm:2.0.7" + checksum: 10c0/a574f97a899f0d9346fa26b528b3f4a9ba6dcb9172288efb6b4314d8486470ed53d2f538200f66a25b843c6e0cbf83688c6d5174a8dc6eca853b291b09609c5a + languageName: node + linkType: hard + +"@inquirer/confirm@npm:^6.0.11": + version: 6.1.1 + resolution: "@inquirer/confirm@npm:6.1.1" + dependencies: + "@inquirer/core": "npm:^11.2.1" + "@inquirer/type": "npm:^4.0.7" + peerDependencies: + "@types/node": ">=18" + peerDependenciesMeta: + "@types/node": + optional: true + checksum: 10c0/4684406161c09327df830b4026f3165b31e13831276d215051586408ed434423263b15686393ce95a4b55058c1b7f9b08aa4b66f5ac930b47523fff75051d36f + languageName: node + linkType: hard + +"@inquirer/core@npm:^11.2.1": + version: 11.2.1 + resolution: "@inquirer/core@npm:11.2.1" + dependencies: + "@inquirer/ansi": "npm:^2.0.7" + "@inquirer/figures": "npm:^2.0.7" + "@inquirer/type": "npm:^4.0.7" + cli-width: "npm:^4.1.0" + fast-wrap-ansi: "npm:^0.2.0" + mute-stream: "npm:^3.0.0" + signal-exit: "npm:^4.1.0" + peerDependencies: + "@types/node": ">=18" + peerDependenciesMeta: + "@types/node": + optional: true + checksum: 10c0/b5be386cecd9e441ac2f9d3417a6ae1c4658b3ee6cdf5dae791211400f4de158851f81fca2245e2062833716f95366b9e1717770828cb7365e756c16e822f0d2 + languageName: node + linkType: hard + +"@inquirer/figures@npm:^2.0.7": + version: 2.0.7 + resolution: "@inquirer/figures@npm:2.0.7" + checksum: 10c0/e0573dc9ad25fa3628d5164745e52852d8cd832a9918605b7716df2e37a0005a0aaf40b6d81cef2ca09cb708b200e61b82d1dcd17003f572577e233c19a9ec7b + languageName: node + linkType: hard + +"@inquirer/type@npm:^4.0.7": + version: 4.0.7 + resolution: "@inquirer/type@npm:4.0.7" + peerDependencies: + "@types/node": ">=18" + peerDependenciesMeta: + "@types/node": + optional: true + checksum: 10c0/80678ac1c6e19ce309909e4a54a69adc95697ea3abc2cb92f17b1bc52f4caadbcb4003ae7339fb5a70c0d36d3bde975e1bb4450069662f41c953a0d28695bb70 + languageName: node + linkType: hard + "@internationalized/date@npm:^3.12.1": version: 3.12.1 resolution: "@internationalized/date@npm:3.12.1" @@ -449,7 +663,7 @@ __metadata: languageName: node linkType: hard -"@jridgewell/sourcemap-codec@npm:^1.4.14, @jridgewell/sourcemap-codec@npm:^1.5.0": +"@jridgewell/sourcemap-codec@npm:^1.4.14, @jridgewell/sourcemap-codec@npm:^1.5.0, @jridgewell/sourcemap-codec@npm:^1.5.5": version: 1.5.5 resolution: "@jridgewell/sourcemap-codec@npm:1.5.5" checksum: 10c0/f9e538f302b63c0ebc06eecb1dd9918dd4289ed36147a0ddce35d6ea4d7ebbda243cda7b2213b6a5e1d8087a298d5cf630fb2bd39329cdecb82017023f6081a0 @@ -466,6 +680,20 @@ __metadata: languageName: node linkType: hard +"@mswjs/interceptors@npm:^0.41.3": + version: 0.41.9 + resolution: "@mswjs/interceptors@npm:0.41.9" + dependencies: + "@open-draft/deferred-promise": "npm:^2.2.0" + "@open-draft/logger": "npm:^0.3.0" + "@open-draft/until": "npm:^2.0.0" + is-node-process: "npm:^1.2.0" + outvariant: "npm:^1.4.3" + strict-event-emitter: "npm:^0.5.1" + checksum: 10c0/2efff40877e07ce29846be76c2683177308a72a3ccfe4c096b496412a279acd92b5b9cdad40ad71c36b149a4a7d9e9b4e7d295893a8ba9661b43334f5784ecd8 + languageName: node + linkType: hard + "@napi-rs/wasm-runtime@npm:^1.1.4": version: 1.1.4 resolution: "@napi-rs/wasm-runtime@npm:1.1.4" @@ -478,6 +706,37 @@ __metadata: languageName: node linkType: hard +"@open-draft/deferred-promise@npm:^2.2.0": + version: 2.2.0 + resolution: "@open-draft/deferred-promise@npm:2.2.0" + checksum: 10c0/eafc1b1d0fc8edb5e1c753c5e0f3293410b40dde2f92688211a54806d4136887051f39b98c1950370be258483deac9dfd17cf8b96557553765198ef2547e4549 + languageName: node + linkType: hard + +"@open-draft/deferred-promise@npm:^3.0.0": + version: 3.0.0 + resolution: "@open-draft/deferred-promise@npm:3.0.0" + checksum: 10c0/4dd697e55495e436be9536413cc9975e792e9ca7472e81e3d3d69e9b65cb678465aac90b463ac02f2b490c0581c4e9aa8a33d2a5857decbe2c6d9ffb310f8e1f + languageName: node + linkType: hard + +"@open-draft/logger@npm:^0.3.0": + version: 0.3.0 + resolution: "@open-draft/logger@npm:0.3.0" + dependencies: + is-node-process: "npm:^1.2.0" + outvariant: "npm:^1.4.0" + checksum: 10c0/90010647b22e9693c16258f4f9adb034824d1771d3baa313057b9a37797f571181005bc50415a934eaf7c891d90ff71dcd7a9d5048b0b6bb438f31bef2c7c5c1 + languageName: node + linkType: hard + +"@open-draft/until@npm:^2.0.0": + version: 2.1.0 + resolution: "@open-draft/until@npm:2.1.0" + checksum: 10c0/61d3f99718dd86bb393fee2d7a785f961dcaf12f2055f0c693b27f4d0cd5f7a03d498a6d9289773b117590d794a43cd129366fd8e99222e4832f67b1653d54cf + languageName: node + linkType: hard + "@oxc-project/types@npm:=0.127.0": version: 0.127.0 resolution: "@oxc-project/types@npm:0.127.0" @@ -485,6 +744,13 @@ __metadata: languageName: node linkType: hard +"@oxc-project/types@npm:=0.133.0": + version: 0.133.0 + resolution: "@oxc-project/types@npm:0.133.0" + checksum: 10c0/70c57ba58644f7ec217b670c301801f4d06995f4ccdba6b2bd106ea3e5ee49d616573e6ef8d55530b87571a960696543687f3850e87ad173d3f88965c30cdd63 + languageName: node + linkType: hard + "@parcel/watcher-android-arm64@npm:2.5.6": version: 2.5.6 resolution: "@parcel/watcher-android-arm64@npm:2.5.6" @@ -707,6 +973,13 @@ __metadata: languageName: node linkType: hard +"@rolldown/binding-android-arm64@npm:1.0.3": + version: 1.0.3 + resolution: "@rolldown/binding-android-arm64@npm:1.0.3" + conditions: os=android & cpu=arm64 + languageName: node + linkType: hard + "@rolldown/binding-darwin-arm64@npm:1.0.0-rc.17": version: 1.0.0-rc.17 resolution: "@rolldown/binding-darwin-arm64@npm:1.0.0-rc.17" @@ -714,6 +987,13 @@ __metadata: languageName: node linkType: hard +"@rolldown/binding-darwin-arm64@npm:1.0.3": + version: 1.0.3 + resolution: "@rolldown/binding-darwin-arm64@npm:1.0.3" + conditions: os=darwin & cpu=arm64 + languageName: node + linkType: hard + "@rolldown/binding-darwin-x64@npm:1.0.0-rc.17": version: 1.0.0-rc.17 resolution: "@rolldown/binding-darwin-x64@npm:1.0.0-rc.17" @@ -721,6 +1001,13 @@ __metadata: languageName: node linkType: hard +"@rolldown/binding-darwin-x64@npm:1.0.3": + version: 1.0.3 + resolution: "@rolldown/binding-darwin-x64@npm:1.0.3" + conditions: os=darwin & cpu=x64 + languageName: node + linkType: hard + "@rolldown/binding-freebsd-x64@npm:1.0.0-rc.17": version: 1.0.0-rc.17 resolution: "@rolldown/binding-freebsd-x64@npm:1.0.0-rc.17" @@ -728,6 +1015,13 @@ __metadata: languageName: node linkType: hard +"@rolldown/binding-freebsd-x64@npm:1.0.3": + version: 1.0.3 + resolution: "@rolldown/binding-freebsd-x64@npm:1.0.3" + conditions: os=freebsd & cpu=x64 + languageName: node + linkType: hard + "@rolldown/binding-linux-arm-gnueabihf@npm:1.0.0-rc.17": version: 1.0.0-rc.17 resolution: "@rolldown/binding-linux-arm-gnueabihf@npm:1.0.0-rc.17" @@ -735,6 +1029,13 @@ __metadata: languageName: node linkType: hard +"@rolldown/binding-linux-arm-gnueabihf@npm:1.0.3": + version: 1.0.3 + resolution: "@rolldown/binding-linux-arm-gnueabihf@npm:1.0.3" + conditions: os=linux & cpu=arm + languageName: node + linkType: hard + "@rolldown/binding-linux-arm64-gnu@npm:1.0.0-rc.17": version: 1.0.0-rc.17 resolution: "@rolldown/binding-linux-arm64-gnu@npm:1.0.0-rc.17" @@ -742,6 +1043,13 @@ __metadata: languageName: node linkType: hard +"@rolldown/binding-linux-arm64-gnu@npm:1.0.3": + version: 1.0.3 + resolution: "@rolldown/binding-linux-arm64-gnu@npm:1.0.3" + conditions: os=linux & cpu=arm64 & libc=glibc + languageName: node + linkType: hard + "@rolldown/binding-linux-arm64-musl@npm:1.0.0-rc.17": version: 1.0.0-rc.17 resolution: "@rolldown/binding-linux-arm64-musl@npm:1.0.0-rc.17" @@ -749,6 +1057,13 @@ __metadata: languageName: node linkType: hard +"@rolldown/binding-linux-arm64-musl@npm:1.0.3": + version: 1.0.3 + resolution: "@rolldown/binding-linux-arm64-musl@npm:1.0.3" + conditions: os=linux & cpu=arm64 & libc=musl + languageName: node + linkType: hard + "@rolldown/binding-linux-ppc64-gnu@npm:1.0.0-rc.17": version: 1.0.0-rc.17 resolution: "@rolldown/binding-linux-ppc64-gnu@npm:1.0.0-rc.17" @@ -756,6 +1071,13 @@ __metadata: languageName: node linkType: hard +"@rolldown/binding-linux-ppc64-gnu@npm:1.0.3": + version: 1.0.3 + resolution: "@rolldown/binding-linux-ppc64-gnu@npm:1.0.3" + conditions: os=linux & cpu=ppc64 & libc=glibc + languageName: node + linkType: hard + "@rolldown/binding-linux-s390x-gnu@npm:1.0.0-rc.17": version: 1.0.0-rc.17 resolution: "@rolldown/binding-linux-s390x-gnu@npm:1.0.0-rc.17" @@ -763,6 +1085,13 @@ __metadata: languageName: node linkType: hard +"@rolldown/binding-linux-s390x-gnu@npm:1.0.3": + version: 1.0.3 + resolution: "@rolldown/binding-linux-s390x-gnu@npm:1.0.3" + conditions: os=linux & cpu=s390x & libc=glibc + languageName: node + linkType: hard + "@rolldown/binding-linux-x64-gnu@npm:1.0.0-rc.17": version: 1.0.0-rc.17 resolution: "@rolldown/binding-linux-x64-gnu@npm:1.0.0-rc.17" @@ -770,6 +1099,13 @@ __metadata: languageName: node linkType: hard +"@rolldown/binding-linux-x64-gnu@npm:1.0.3": + version: 1.0.3 + resolution: "@rolldown/binding-linux-x64-gnu@npm:1.0.3" + conditions: os=linux & cpu=x64 & libc=glibc + languageName: node + linkType: hard + "@rolldown/binding-linux-x64-musl@npm:1.0.0-rc.17": version: 1.0.0-rc.17 resolution: "@rolldown/binding-linux-x64-musl@npm:1.0.0-rc.17" @@ -777,6 +1113,13 @@ __metadata: languageName: node linkType: hard +"@rolldown/binding-linux-x64-musl@npm:1.0.3": + version: 1.0.3 + resolution: "@rolldown/binding-linux-x64-musl@npm:1.0.3" + conditions: os=linux & cpu=x64 & libc=musl + languageName: node + linkType: hard + "@rolldown/binding-openharmony-arm64@npm:1.0.0-rc.17": version: 1.0.0-rc.17 resolution: "@rolldown/binding-openharmony-arm64@npm:1.0.0-rc.17" @@ -784,6 +1127,13 @@ __metadata: languageName: node linkType: hard +"@rolldown/binding-openharmony-arm64@npm:1.0.3": + version: 1.0.3 + resolution: "@rolldown/binding-openharmony-arm64@npm:1.0.3" + conditions: os=openharmony & cpu=arm64 + languageName: node + linkType: hard + "@rolldown/binding-wasm32-wasi@npm:1.0.0-rc.17": version: 1.0.0-rc.17 resolution: "@rolldown/binding-wasm32-wasi@npm:1.0.0-rc.17" @@ -795,6 +1145,17 @@ __metadata: languageName: node linkType: hard +"@rolldown/binding-wasm32-wasi@npm:1.0.3": + version: 1.0.3 + resolution: "@rolldown/binding-wasm32-wasi@npm:1.0.3" + dependencies: + "@emnapi/core": "npm:1.10.0" + "@emnapi/runtime": "npm:1.10.0" + "@napi-rs/wasm-runtime": "npm:^1.1.4" + conditions: cpu=wasm32 + languageName: node + linkType: hard + "@rolldown/binding-win32-arm64-msvc@npm:1.0.0-rc.17": version: 1.0.0-rc.17 resolution: "@rolldown/binding-win32-arm64-msvc@npm:1.0.0-rc.17" @@ -802,6 +1163,13 @@ __metadata: languageName: node linkType: hard +"@rolldown/binding-win32-arm64-msvc@npm:1.0.3": + version: 1.0.3 + resolution: "@rolldown/binding-win32-arm64-msvc@npm:1.0.3" + conditions: os=win32 & cpu=arm64 + languageName: node + linkType: hard + "@rolldown/binding-win32-x64-msvc@npm:1.0.0-rc.17": version: 1.0.0-rc.17 resolution: "@rolldown/binding-win32-x64-msvc@npm:1.0.0-rc.17" @@ -809,6 +1177,13 @@ __metadata: languageName: node linkType: hard +"@rolldown/binding-win32-x64-msvc@npm:1.0.3": + version: 1.0.3 + resolution: "@rolldown/binding-win32-x64-msvc@npm:1.0.3" + conditions: os=win32 & cpu=x64 + languageName: node + linkType: hard + "@rolldown/pluginutils@npm:1.0.0-rc.17": version: 1.0.0-rc.17 resolution: "@rolldown/pluginutils@npm:1.0.0-rc.17" @@ -823,6 +1198,20 @@ __metadata: languageName: node linkType: hard +"@rolldown/pluginutils@npm:^1.0.0": + version: 1.0.1 + resolution: "@rolldown/pluginutils@npm:1.0.1" + checksum: 10c0/99d9b06d90196823e4d8c841f258db7a16e5dbba5824a2962b05d907b79f1ba929d56f22dd744fd530936e568c865ee56a719dc31e57e13bc0a8eb4764a8d8dd + languageName: node + linkType: hard + +"@standard-schema/spec@npm:^1.1.0": + version: 1.1.0 + resolution: "@standard-schema/spec@npm:1.1.0" + checksum: 10c0/d90f55acde4b2deb983529c87e8025fa693de1a5e8b49ecc6eb84d1fd96328add0e03d7d551442156c7432fd78165b2c26ff561b970a9a881f046abb78d6a526 + languageName: node + linkType: hard + "@swc/helpers@npm:^0.5.0": version: 0.5.21 resolution: "@swc/helpers@npm:0.5.21" @@ -907,6 +1296,65 @@ __metadata: languageName: node linkType: hard +"@testing-library/dom@npm:^10.4.1": + version: 10.4.1 + resolution: "@testing-library/dom@npm:10.4.1" + dependencies: + "@babel/code-frame": "npm:^7.10.4" + "@babel/runtime": "npm:^7.12.5" + "@types/aria-query": "npm:^5.0.1" + aria-query: "npm:5.3.0" + dom-accessibility-api: "npm:^0.5.9" + lz-string: "npm:^1.5.0" + picocolors: "npm:1.1.1" + pretty-format: "npm:^27.0.2" + checksum: 10c0/19ce048012d395ad0468b0dbcc4d0911f6f9e39464d7a8464a587b29707eed5482000dad728f5acc4ed314d2f4d54f34982999a114d2404f36d048278db815b1 + languageName: node + linkType: hard + +"@testing-library/jest-dom@npm:^6.9.1": + version: 6.9.1 + resolution: "@testing-library/jest-dom@npm:6.9.1" + dependencies: + "@adobe/css-tools": "npm:^4.4.0" + aria-query: "npm:^5.0.0" + css.escape: "npm:^1.5.1" + dom-accessibility-api: "npm:^0.6.3" + picocolors: "npm:^1.1.1" + redent: "npm:^3.0.0" + checksum: 10c0/4291ebd2f0f38d14cefac142c56c337941775a5807e2a3d6f1a14c2fbd6be76a18e498ed189e95bedc97d9e8cf1738049bc76c85b5bc5e23fae7c9e10f7b3a12 + languageName: node + linkType: hard + +"@testing-library/react@npm:^16.3.2": + version: 16.3.2 + resolution: "@testing-library/react@npm:16.3.2" + dependencies: + "@babel/runtime": "npm:^7.12.5" + 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 + checksum: 10c0/f9c7f0915e1b5f7b750e6c7d8b51f091b8ae7ea99bacb761d7b8505ba25de9cfcb749a0f779f1650fb268b499dd79165dc7e1ee0b8b4cb63430d3ddc81ffe044 + languageName: node + linkType: hard + +"@testing-library/user-event@npm:^14.6.1": + version: 14.6.1 + resolution: "@testing-library/user-event@npm:14.6.1" + peerDependencies: + "@testing-library/dom": ">=7.21.4" + checksum: 10c0/75fea130a52bf320d35d46ed54f3eec77e71a56911b8b69a3fe29497b0b9947b2dc80d30f04054ad4ce7f577856ae3e5397ea7dff0ef14944d3909784c7a93fe + languageName: node + linkType: hard + "@tybys/wasm-util@npm:^0.10.1": version: 0.10.1 resolution: "@tybys/wasm-util@npm:0.10.1" @@ -916,6 +1364,13 @@ __metadata: languageName: node linkType: hard +"@types/aria-query@npm:^5.0.1": + version: 5.0.4 + resolution: "@types/aria-query@npm:5.0.4" + checksum: 10c0/dc667bc6a3acc7bba2bccf8c23d56cb1f2f4defaa704cfef595437107efaa972d3b3db9ec1d66bc2711bfc35086821edd32c302bffab36f2e79b97f312069f08 + languageName: node + linkType: hard + "@types/bootstrap@npm:^5": version: 5.2.10 resolution: "@types/bootstrap@npm:5.2.10" @@ -925,6 +1380,30 @@ __metadata: languageName: node linkType: hard +"@types/chai@npm:^5.2.2": + version: 5.2.3 + resolution: "@types/chai@npm:5.2.3" + dependencies: + "@types/deep-eql": "npm:*" + assertion-error: "npm:^2.0.1" + checksum: 10c0/e0ef1de3b6f8045a5e473e867c8565788c444271409d155588504840ad1a53611011f85072188c2833941189400228c1745d78323dac13fcede9c2b28bacfb2f + languageName: node + linkType: hard + +"@types/deep-eql@npm:*": + version: 4.0.2 + resolution: "@types/deep-eql@npm:4.0.2" + checksum: 10c0/bf3f811843117900d7084b9d0c852da9a044d12eb40e6de73b552598a6843c21291a8a381b0532644574beecd5e3491c5ff3a0365ab86b15d59862c025384844 + languageName: node + linkType: hard + +"@types/estree@npm:^1.0.0": + version: 1.0.9 + resolution: "@types/estree@npm:1.0.9" + checksum: 10c0/3ad3286ca2988cd550dafb8f2ad599c8474868e954fa601a36655bdfefd8039f7c714b8c1c7f2ae219ffbd58bd4660e66fa7479a0120fc02d4777057d4865387 + languageName: node + linkType: hard + "@types/estree@npm:^1.0.6": version: 1.0.8 resolution: "@types/estree@npm:1.0.8" @@ -948,6 +1427,15 @@ __metadata: languageName: node linkType: hard +"@types/node@npm:*": + version: 25.9.1 + resolution: "@types/node@npm:25.9.1" + dependencies: + undici-types: "npm:>=7.24.0 <7.24.7" + checksum: 10c0/9a04682842bebbcf21a1779dfeab9aa733d7bd7bbc0a0edb641ab3a9a3d43eac543225acf669c334f458f1956443ebc072bc3c72840c543b8b356cab5c82d456 + languageName: node + linkType: hard + "@types/node@npm:^25.6.0": version: 25.6.0 resolution: "@types/node@npm:25.6.0" @@ -991,9 +1479,25 @@ __metadata: languageName: node linkType: hard -"@types/warning@npm:^3.0.3": - version: 3.0.4 - resolution: "@types/warning@npm:3.0.4" +"@types/set-cookie-parser@npm:^2.4.10": + version: 2.4.10 + resolution: "@types/set-cookie-parser@npm:2.4.10" + dependencies: + "@types/node": "npm:*" + checksum: 10c0/010b0c582ea70a2088618b4725808e80c30cce296c19ec58e51d94e0fd1038201b7b99238bf3ea74e1894163c8037d10a4f1729de62b2801ce240ff070f43e76 + languageName: node + linkType: hard + +"@types/statuses@npm:^2.0.6": + version: 2.0.6 + resolution: "@types/statuses@npm:2.0.6" + checksum: 10c0/dd88c220b0e2c6315686289525fd61472d2204d2e4bef4941acfb76bda01d3066f749ac74782aab5b537a45314fcd7d6261eefa40b6ec872691f5803adaa608d + languageName: node + linkType: hard + +"@types/warning@npm:^3.0.3": + version: 3.0.4 + resolution: "@types/warning@npm:3.0.4" checksum: 10c0/88afbe2e23c04dd1441febef5557b7d111e38248cc731956b1a13708c433c70c10b14177ff7bb3392abd4dab7d474edd770e0737e9385427e43211a55d551bce languageName: node linkType: hard @@ -1151,6 +1655,88 @@ __metadata: languageName: node linkType: hard +"@vitest/expect@npm:4.1.8": + version: 4.1.8 + resolution: "@vitest/expect@npm:4.1.8" + dependencies: + "@standard-schema/spec": "npm:^1.1.0" + "@types/chai": "npm:^5.2.2" + "@vitest/spy": "npm:4.1.8" + "@vitest/utils": "npm:4.1.8" + chai: "npm:^6.2.2" + tinyrainbow: "npm:^3.1.0" + checksum: 10c0/f7bf6c720d2427c3bd0b35472ebd84d963be7d09ecf52a0fb05e8c4d5d0c9ee164a8c28eee6360947be1b245b47faefab54560cb98e5cb678c1c1074260b9149 + languageName: node + linkType: hard + +"@vitest/mocker@npm:4.1.8": + version: 4.1.8 + resolution: "@vitest/mocker@npm:4.1.8" + dependencies: + "@vitest/spy": "npm:4.1.8" + estree-walker: "npm:^3.0.3" + magic-string: "npm:^0.30.21" + peerDependencies: + msw: ^2.4.9 + vite: ^6.0.0 || ^7.0.0 || ^8.0.0 + peerDependenciesMeta: + msw: + optional: true + vite: + optional: true + checksum: 10c0/f8cb2b8b55dc2cba0b2399aeee528b0187042f22cbc2d50a4fd6141f5aa246ebc41700f45dd1d73eca44ddfb57dcde48b2eb317bfbb1198f5ab2cc4fd04b2ea0 + languageName: node + linkType: hard + +"@vitest/pretty-format@npm:4.1.8": + version: 4.1.8 + resolution: "@vitest/pretty-format@npm:4.1.8" + dependencies: + tinyrainbow: "npm:^3.1.0" + checksum: 10c0/553c456692a4b9ae13cd116c234c74b4495e0f1a0d5c51ffc3fab8ea085e3550769967e29db79bdac0cf127b1bf88b7f70bfba3dcc72be6bddf834433e30cc91 + languageName: node + linkType: hard + +"@vitest/runner@npm:4.1.8": + version: 4.1.8 + resolution: "@vitest/runner@npm:4.1.8" + dependencies: + "@vitest/utils": "npm:4.1.8" + pathe: "npm:^2.0.3" + checksum: 10c0/706808a4b7b95ea9a9268fc152dd39e15a9a754f37c7990aea167486a9094caa913dae454771ae02c18dccfabd667f8cc38eed33a1307a79d32a89878b5bcce1 + languageName: node + linkType: hard + +"@vitest/snapshot@npm:4.1.8": + version: 4.1.8 + resolution: "@vitest/snapshot@npm:4.1.8" + dependencies: + "@vitest/pretty-format": "npm:4.1.8" + "@vitest/utils": "npm:4.1.8" + magic-string: "npm:^0.30.21" + pathe: "npm:^2.0.3" + checksum: 10c0/ba4c32112491d42d24986f921c50ede5edbdb4b7eafa16c72cf8d2c9ecc44121fdb3d9365236747a9841f0d6776affc6457470fcbb082df9dbc28c24792a0c6d + languageName: node + linkType: hard + +"@vitest/spy@npm:4.1.8": + version: 4.1.8 + resolution: "@vitest/spy@npm:4.1.8" + checksum: 10c0/3c10c0325a09d16bc0e77c0be96c47c15416186e33332880c0d1dd0a51d51a866091067b81f2a2ef6fb422a7760e6cf15c04d91a0eca4d59f62e8c8401fa53fc + languageName: node + linkType: hard + +"@vitest/utils@npm:4.1.8": + version: 4.1.8 + resolution: "@vitest/utils@npm:4.1.8" + dependencies: + "@vitest/pretty-format": "npm:4.1.8" + convert-source-map: "npm:^2.0.0" + tinyrainbow: "npm:^3.1.0" + checksum: 10c0/acda9d3d640c1ebc81afb358ac30589d7d7d583af81e2d09419f0af9cbe41f3ce0b90527326943bf0da51614be5fc31afcd32259f6beb32b3417999d6ef380f3 + languageName: node + linkType: hard + "abbrev@npm:^4.0.0": version: 4.0.0 resolution: "abbrev@npm:4.0.0" @@ -1188,7 +1774,14 @@ __metadata: languageName: node linkType: hard -"ansi-styles@npm:^4.1.0": +"ansi-regex@npm:^5.0.1": + version: 5.0.1 + resolution: "ansi-regex@npm:5.0.1" + checksum: 10c0/9a64bb8627b434ba9327b60c027742e5d17ac69277960d041898596271d992d4d52ba7267a63ca10232e29f6107fc8a835f6ce8d719b88c5f8493f8254813737 + languageName: node + linkType: hard + +"ansi-styles@npm:^4.0.0, ansi-styles@npm:^4.1.0": version: 4.3.0 resolution: "ansi-styles@npm:4.3.0" dependencies: @@ -1197,6 +1790,13 @@ __metadata: languageName: node linkType: hard +"ansi-styles@npm:^5.0.0": + version: 5.2.0 + resolution: "ansi-styles@npm:5.2.0" + checksum: 10c0/9c4ca80eb3c2fb7b33841c210d2f20807f40865d27008d7c3f707b7f95cab7d67462a565e2388ac3285b71cb3d9bb2173de8da37c57692a362885ec34d6e27df + languageName: node + linkType: hard + "app@workspace:.": version: 0.0.0-use.local resolution: "app@workspace:." @@ -1211,6 +1811,10 @@ __metadata: "@tanstack/react-query-devtools": "npm:^5.100.9" "@tanstack/react-table": "npm:^8.21.3" "@tanstack/react-virtual": "npm:^3.13.24" + "@testing-library/dom": "npm:^10.4.1" + "@testing-library/jest-dom": "npm:^6.9.1" + "@testing-library/react": "npm:^16.3.2" + "@testing-library/user-event": "npm:^14.6.1" "@types/bootstrap": "npm:^5" "@types/faker": "npm:^6" "@types/node": "npm:^25.6.0" @@ -1223,6 +1827,8 @@ __metadata: eslint-plugin-react: "npm:^7.37.5" eslint-plugin-react-hooks: "npm:^7.0.1" globals: "npm:^17.6.0" + jsdom: "npm:^29.1.1" + msw: "npm:^2.14.6" prettier: "npm:3.8.3" react: "npm:19.2.3" react-bootstrap: "npm:^2.10.10" @@ -1234,6 +1840,7 @@ __metadata: typescript-eslint: "npm:^8.53.1" vite: "npm:^8.0.0" vite-plugin-ruby: "npm:^5.2.0" + vitest: "npm:^4.1.8" zustand: "npm:^5.0.13" languageName: unknown linkType: soft @@ -1254,6 +1861,22 @@ __metadata: languageName: node linkType: hard +"aria-query@npm:5.3.0": + version: 5.3.0 + resolution: "aria-query@npm:5.3.0" + dependencies: + dequal: "npm:^2.0.3" + checksum: 10c0/2bff0d4eba5852a9dd578ecf47eaef0e82cc52569b48469b0aac2db5145db0b17b7a58d9e01237706d1e14b7a1b0ac9b78e9c97027ad97679dd8f91b85da1469 + languageName: node + linkType: hard + +"aria-query@npm:^5.0.0": + version: 5.3.2 + resolution: "aria-query@npm:5.3.2" + checksum: 10c0/003c7e3e2cff5540bf7a7893775fc614de82b0c5dde8ae823d47b7a28a9d4da1f7ed85f340bdb93d5649caa927755f0e31ecc7ab63edfdfc00c8ef07e505e03e + languageName: node + linkType: hard + "array-buffer-byte-length@npm:^1.0.1, array-buffer-byte-length@npm:^1.0.2": version: 1.0.2 resolution: "array-buffer-byte-length@npm:1.0.2" @@ -1346,6 +1969,13 @@ __metadata: languageName: node linkType: hard +"assertion-error@npm:^2.0.1": + version: 2.0.1 + resolution: "assertion-error@npm:2.0.1" + checksum: 10c0/bbbcb117ac6480138f8c93cf7f535614282dea9dc828f540cdece85e3c665e8f78958b96afac52f29ff883c72638e6a87d469ecc9fe5bc902df03ed24a55dba8 + languageName: node + linkType: hard + "async-function@npm:^1.0.0": version: 1.0.0 resolution: "async-function@npm:1.0.0" @@ -1392,6 +2022,15 @@ __metadata: languageName: node linkType: hard +"bidi-js@npm:^1.0.3": + version: 1.0.3 + resolution: "bidi-js@npm:1.0.3" + dependencies: + require-from-string: "npm:^2.0.2" + checksum: 10c0/fdddea4aa4120a34285486f2267526cd9298b6e8b773ad25e765d4f104b6d7437ab4ba542e6939e3ac834a7570bcf121ee2cf6d3ae7cd7082c4b5bedc8f271e1 + languageName: node + linkType: hard + "bootstrap@npm:^5.3.8": version: 5.3.8 resolution: "bootstrap@npm:5.3.8" @@ -1481,6 +2120,13 @@ __metadata: languageName: node linkType: hard +"chai@npm:^6.2.2": + version: 6.2.2 + resolution: "chai@npm:6.2.2" + checksum: 10c0/e6c69e5f0c11dffe6ea13d0290936ebb68fcc1ad688b8e952e131df6a6d5797d5e860bc55cef1aca2e950c3e1f96daf79e9d5a70fb7dbaab4e46355e2635ed53 + languageName: node + linkType: hard + "chalk@npm:^4.0.0": version: 4.1.2 resolution: "chalk@npm:4.1.2" @@ -1514,6 +2160,24 @@ __metadata: languageName: node linkType: hard +"cli-width@npm:^4.1.0": + version: 4.1.0 + resolution: "cli-width@npm:4.1.0" + checksum: 10c0/1fbd56413578f6117abcaf858903ba1f4ad78370a4032f916745fa2c7e390183a9d9029cf837df320b0fdce8137668e522f60a30a5f3d6529ff3872d265a955f + languageName: node + linkType: hard + +"cliui@npm:^8.0.1": + version: 8.0.1 + resolution: "cliui@npm:8.0.1" + dependencies: + string-width: "npm:^4.2.0" + strip-ansi: "npm:^6.0.1" + wrap-ansi: "npm:^7.0.0" + checksum: 10c0/4bda0f09c340cbb6dfdc1ed508b3ca080f12992c18d68c6be4d9cf51756033d5266e61ec57529e610dacbf4da1c634423b0c1b11037709cc6b09045cbd815df5 + languageName: node + linkType: hard + "clsx@npm:^2.0.0": version: 2.1.1 resolution: "clsx@npm:2.1.1" @@ -1551,7 +2215,7 @@ __metadata: languageName: node linkType: hard -"cookie@npm:^1.0.1": +"cookie@npm:^1.0.1, cookie@npm:^1.1.1": version: 1.1.1 resolution: "cookie@npm:1.1.1" checksum: 10c0/79c4ddc0fcad9c4f045f826f42edf54bcc921a29586a4558b0898277fa89fb47be95bc384c2253f493af7b29500c830da28341274527328f18eba9f58afa112c @@ -1569,6 +2233,23 @@ __metadata: languageName: node linkType: hard +"css-tree@npm:^3.0.0, css-tree@npm:^3.2.1": + version: 3.2.1 + resolution: "css-tree@npm:3.2.1" + dependencies: + mdn-data: "npm:2.27.1" + source-map-js: "npm:^1.2.1" + checksum: 10c0/1f65e9ccaa56112a4706d6f003dd43d777f0dbcf848e66fd320f823192533581f8dd58daa906cb80622658332d50284d6be13b87a6ab4556cbbfe9ef535bbf7e + languageName: node + linkType: hard + +"css.escape@npm:^1.5.1": + version: 1.5.1 + resolution: "css.escape@npm:1.5.1" + checksum: 10c0/5e09035e5bf6c2c422b40c6df2eb1529657a17df37fda5d0433d722609527ab98090baf25b13970ca754079a0f3161dd3dfc0e743563ded8cfa0749d861c1525 + languageName: node + linkType: hard + "csstype@npm:^3.0.2, csstype@npm:^3.2.2": version: 3.2.3 resolution: "csstype@npm:3.2.3" @@ -1576,6 +2257,16 @@ __metadata: languageName: node linkType: hard +"data-urls@npm:^7.0.0": + version: 7.0.0 + resolution: "data-urls@npm:7.0.0" + dependencies: + whatwg-mimetype: "npm:^5.0.0" + whatwg-url: "npm:^16.0.0" + checksum: 10c0/08d88ef50d8966a070ffdaa703e1e4b29f01bb2da364dfbc1612b1c2a4caa8045802c9532d81347b21781100132addb36a585071c8323b12cce97973961dee9f + languageName: node + linkType: hard + "data-view-buffer@npm:^1.0.2": version: 1.0.2 resolution: "data-view-buffer@npm:1.0.2" @@ -1621,6 +2312,13 @@ __metadata: languageName: node linkType: hard +"decimal.js@npm:^10.6.0": + version: 10.6.0 + resolution: "decimal.js@npm:10.6.0" + checksum: 10c0/07d69fbcc54167a340d2d97de95f546f9ff1f69d2b45a02fd7a5292412df3cd9eb7e23065e532a318f5474a2e1bccf8392fdf0443ef467f97f3bf8cb0477e5aa + languageName: node + linkType: hard + "deep-is@npm:^0.1.3": version: 0.1.4 resolution: "deep-is@npm:0.1.4" @@ -1673,6 +2371,20 @@ __metadata: languageName: node linkType: hard +"dom-accessibility-api@npm:^0.5.9": + version: 0.5.16 + resolution: "dom-accessibility-api@npm:0.5.16" + checksum: 10c0/b2c2eda4fae568977cdac27a9f0c001edf4f95a6a6191dfa611e3721db2478d1badc01db5bb4fa8a848aeee13e442a6c2a4386d65ec65a1436f24715a2f8d053 + languageName: node + linkType: hard + +"dom-accessibility-api@npm:^0.6.3": + version: 0.6.3 + resolution: "dom-accessibility-api@npm:0.6.3" + checksum: 10c0/10bee5aa514b2a9a37c87cd81268db607a2e933a050074abc2f6fa3da9080ebed206a320cbc123567f2c3087d22292853bdfdceaffdd4334ffe2af9510b29360 + languageName: node + linkType: hard + "dom-helpers@npm:^5.0.1, dom-helpers@npm:^5.2.0, dom-helpers@npm:^5.2.1": version: 5.2.1 resolution: "dom-helpers@npm:5.2.1" @@ -1701,6 +2413,20 @@ __metadata: languageName: node linkType: hard +"emoji-regex@npm:^8.0.0": + version: 8.0.0 + resolution: "emoji-regex@npm:8.0.0" + checksum: 10c0/b6053ad39951c4cf338f9092d7bfba448cdfd46fe6a2a034700b149ac9ffbc137e361cbd3c442297f86bed2e5f7576c1b54cc0a6bf8ef5106cc62f496af35010 + languageName: node + linkType: hard + +"entities@npm:^8.0.0": + version: 8.0.0 + resolution: "entities@npm:8.0.0" + checksum: 10c0/938e631664c19451823344a351aeeafd74fae2d5fa51e4d5b6ff635afaefd4bacf0f609989888c04c42733f46ffdac15211608267ebb02488005891a4793e94d + languageName: node + linkType: hard + "env-paths@npm:^2.2.0": version: 2.2.1 resolution: "env-paths@npm:2.2.1" @@ -1808,6 +2534,13 @@ __metadata: languageName: node linkType: hard +"es-module-lexer@npm:^2.0.0": + version: 2.1.0 + resolution: "es-module-lexer@npm:2.1.0" + checksum: 10c0/93bcf2454fa72d67fe3ccd0abef8ce7933f5840a319513418a643dd8e9c6aa8f49709cecfae02ded722805dd327232d30723a807cc52e6809d6ac697c62c29fb + languageName: node + linkType: hard + "es-object-atoms@npm:^1.0.0, es-object-atoms@npm:^1.1.1": version: 1.1.1 resolution: "es-object-atoms@npm:1.1.1" @@ -1849,7 +2582,7 @@ __metadata: languageName: node linkType: hard -"escalade@npm:^3.2.0": +"escalade@npm:^3.1.1, escalade@npm:^3.2.0": version: 3.2.0 resolution: "escalade@npm:3.2.0" checksum: 10c0/ced4dd3a78e15897ed3be74e635110bbf3b08877b0a41be50dcb325ee0e0b5f65fc2d50e9845194d7c4633f327e2e1c6cce00a71b617c5673df0374201d67f65 @@ -2033,6 +2766,15 @@ __metadata: languageName: node linkType: hard +"estree-walker@npm:^3.0.3": + version: 3.0.3 + resolution: "estree-walker@npm:3.0.3" + dependencies: + "@types/estree": "npm:^1.0.0" + checksum: 10c0/c12e3c2b2642d2bcae7d5aa495c60fa2f299160946535763969a1c83fc74518ffa9c2cd3a8b69ac56aea547df6a8aac25f729a342992ef0bbac5f1c73e78995d + languageName: node + linkType: hard + "esutils@npm:^2.0.2": version: 2.0.3 resolution: "esutils@npm:2.0.3" @@ -2040,6 +2782,13 @@ __metadata: languageName: node linkType: hard +"expect-type@npm:^1.3.0": + version: 1.3.0 + resolution: "expect-type@npm:1.3.0" + checksum: 10c0/8412b3fe4f392c420ab41dae220b09700e4e47c639a29ba7ba2e83cc6cffd2b4926f7ac9e47d7e277e8f4f02acda76fd6931cb81fd2b382fa9477ef9ada953fd + languageName: node + linkType: hard + "exponential-backoff@npm:^3.1.1": version: 3.1.3 resolution: "exponential-backoff@npm:3.1.3" @@ -2075,6 +2824,31 @@ __metadata: languageName: node linkType: hard +"fast-string-truncated-width@npm:^3.0.2": + version: 3.0.3 + resolution: "fast-string-truncated-width@npm:3.0.3" + checksum: 10c0/043b8663397d14a3880ce4f3407bcda60b40db9bbeafe62863a35d1f9c69ea17c8da3fcd72de235553e6c9cd053128cde9e24ca0d4a7463208f48db3cd23d981 + languageName: node + linkType: hard + +"fast-string-width@npm:^3.0.2": + version: 3.0.2 + resolution: "fast-string-width@npm:3.0.2" + dependencies: + fast-string-truncated-width: "npm:^3.0.2" + checksum: 10c0/c8822d175315bb353ebe782b65214ac53b13e3bf704e03b132ea7bdfa8de6a636375b3ab7a4097545393d109381c37c4f387c72a462c90b61412dbc4632f39a7 + languageName: node + linkType: hard + +"fast-wrap-ansi@npm:^0.2.0": + version: 0.2.2 + resolution: "fast-wrap-ansi@npm:0.2.2" + dependencies: + fast-string-width: "npm:^3.0.2" + checksum: 10c0/1aa7be4f7cb86f4bdb14691cb6bcc0b8df8b3b89df142ade3ae1602332dcf6f990cd750a923cd581ca0847808cb4ec1aa5afaafa7a72f849e87a2a62c98fa370 + languageName: node + linkType: hard + "fdir@npm:^6.5.0": version: 6.5.0 resolution: "fdir@npm:6.5.0" @@ -2193,6 +2967,13 @@ __metadata: languageName: node linkType: hard +"get-caller-file@npm:^2.0.5": + version: 2.0.5 + resolution: "get-caller-file@npm:2.0.5" + checksum: 10c0/c6c7b60271931fa752aeb92f2b47e355eac1af3a2673f47c9589e8f8a41adc74d45551c1bc57b5e66a80609f10ffb72b6f575e4370d61cc3f7f3aaff01757cde + languageName: node + linkType: hard + "get-intrinsic@npm:^1.2.4, get-intrinsic@npm:^1.2.5, get-intrinsic@npm:^1.2.6, get-intrinsic@npm:^1.2.7, get-intrinsic@npm:^1.3.0": version: 1.3.1 resolution: "get-intrinsic@npm:1.3.1" @@ -2282,6 +3063,13 @@ __metadata: languageName: node linkType: hard +"graphql@npm:^16.13.2": + version: 16.14.1 + resolution: "graphql@npm:16.14.1" + checksum: 10c0/a67358b4c6bbda0876903d3f6bbba46058ac7bc00c043ae0b435148e2a00481e9c7716f54f271659786f27f58bf355245216938d98ade980fd141885c907efff + languageName: node + linkType: hard + "has-bigints@npm:^1.0.2": version: 1.1.0 resolution: "has-bigints@npm:1.1.0" @@ -2339,6 +3127,16 @@ __metadata: languageName: node linkType: hard +"headers-polyfill@npm:^5.0.1": + version: 5.0.1 + resolution: "headers-polyfill@npm:5.0.1" + dependencies: + "@types/set-cookie-parser": "npm:^2.4.10" + set-cookie-parser: "npm:^3.0.1" + checksum: 10c0/c269730a88a12c88718037aa71f178601f2b193ba8a37e276b6ced6b8f7e06fc1ac051f2a7acb0a8b4cc878407066555fdcdbb270e90374baaa472cb26af0c30 + languageName: node + linkType: hard + "hermes-estree@npm:0.25.1": version: 0.25.1 resolution: "hermes-estree@npm:0.25.1" @@ -2355,6 +3153,15 @@ __metadata: languageName: node linkType: hard +"html-encoding-sniffer@npm:^6.0.0": + version: 6.0.0 + resolution: "html-encoding-sniffer@npm:6.0.0" + dependencies: + "@exodus/bytes": "npm:^1.6.0" + checksum: 10c0/66dc3f6f5539cc3beb814fcbfae7eacf4ec38cf824d6e1425b72039b51a40f4456bd8541ba66f4f4fe09cdf885ab5cd5bae6ec6339d6895a930b2fdb83c53025 + languageName: node + linkType: hard + "ignore@npm:^5.2.0": version: 5.3.2 resolution: "ignore@npm:5.3.2" @@ -2393,6 +3200,13 @@ __metadata: languageName: node linkType: hard +"indent-string@npm:^4.0.0": + version: 4.0.0 + resolution: "indent-string@npm:4.0.0" + checksum: 10c0/1e1904ddb0cb3d6cce7cd09e27a90184908b7a5d5c21b92e232c93579d314f0b83c246ffb035493d0504b1e9147ba2c9b21df0030f48673fba0496ecd698161f + languageName: node + linkType: hard + "internal-slot@npm:^1.1.0": version: 1.1.0 resolution: "internal-slot@npm:1.1.0" @@ -2509,6 +3323,13 @@ __metadata: languageName: node linkType: hard +"is-fullwidth-code-point@npm:^3.0.0": + version: 3.0.0 + resolution: "is-fullwidth-code-point@npm:3.0.0" + checksum: 10c0/bb11d825e049f38e04c06373a8d72782eee0205bda9d908cc550ccb3c59b99d750ff9537982e01733c1c94a58e35400661f57042158ff5e8f3e90cf936daf0fc + languageName: node + linkType: hard + "is-generator-function@npm:^1.0.10": version: 1.1.2 resolution: "is-generator-function@npm:1.1.2" @@ -2545,6 +3366,13 @@ __metadata: languageName: node linkType: hard +"is-node-process@npm:^1.2.0": + version: 1.2.0 + resolution: "is-node-process@npm:1.2.0" + checksum: 10c0/5b24fda6776d00e42431d7bcd86bce81cb0b6cabeb944142fe7b077a54ada2e155066ad06dbe790abdb397884bdc3151e04a9707b8cd185099efbc79780573ed + languageName: node + linkType: hard + "is-number-object@npm:^1.1.1": version: 1.1.1 resolution: "is-number-object@npm:1.1.1" @@ -2555,6 +3383,13 @@ __metadata: languageName: node linkType: hard +"is-potential-custom-element-name@npm:^1.0.1": + version: 1.0.1 + resolution: "is-potential-custom-element-name@npm:1.0.1" + checksum: 10c0/b73e2f22bc863b0939941d369486d308b43d7aef1f9439705e3582bfccaa4516406865e32c968a35f97a99396dac84e2624e67b0a16b0a15086a785e16ce7db9 + languageName: node + linkType: hard + "is-regex@npm:^1.2.1": version: 1.2.1 resolution: "is-regex@npm:1.2.1" @@ -2692,6 +3527,40 @@ __metadata: languageName: node linkType: hard +"jsdom@npm:^29.1.1": + version: 29.1.1 + resolution: "jsdom@npm:29.1.1" + dependencies: + "@asamuzakjp/css-color": "npm:^5.1.11" + "@asamuzakjp/dom-selector": "npm:^7.1.1" + "@bramus/specificity": "npm:^2.4.2" + "@csstools/css-syntax-patches-for-csstree": "npm:^1.1.3" + "@exodus/bytes": "npm:^1.15.0" + css-tree: "npm:^3.2.1" + data-urls: "npm:^7.0.0" + decimal.js: "npm:^10.6.0" + html-encoding-sniffer: "npm:^6.0.0" + is-potential-custom-element-name: "npm:^1.0.1" + lru-cache: "npm:^11.3.5" + parse5: "npm:^8.0.1" + saxes: "npm:^6.0.0" + symbol-tree: "npm:^3.2.4" + tough-cookie: "npm:^6.0.1" + undici: "npm:^7.25.0" + w3c-xmlserializer: "npm:^5.0.0" + webidl-conversions: "npm:^8.0.1" + whatwg-mimetype: "npm:^5.0.0" + whatwg-url: "npm:^16.0.1" + xml-name-validator: "npm:^5.0.0" + peerDependencies: + canvas: ^3.0.0 + peerDependenciesMeta: + canvas: + optional: true + checksum: 10c0/20e2174b09d9d06393cb48e1392b7a1cb7191d6656a6f7b3b8fbf9853b4ab0ef60b4a42c2c55f71b55ca5da50ffa75bcdc6986210963182e7993c6f9cd4f499b + languageName: node + linkType: hard + "jsesc@npm:^3.0.2": version: 3.1.0 resolution: "jsesc@npm:3.1.0" @@ -2909,6 +3778,13 @@ __metadata: languageName: node linkType: hard +"lru-cache@npm:^11.3.5": + version: 11.5.1 + resolution: "lru-cache@npm:11.5.1" + checksum: 10c0/7b341cea79a8efe9c6a6f20c8757a77eca5b25d7ff983ccf4e11e547b81f6787824baa1c84705251dff84ab4ffac85717ac354b9d02e465f86a9f8b166409979 + languageName: node + linkType: hard + "lru-cache@npm:^5.1.1": version: 5.1.1 resolution: "lru-cache@npm:5.1.1" @@ -2918,6 +3794,24 @@ __metadata: languageName: node linkType: hard +"lz-string@npm:^1.5.0": + version: 1.5.0 + resolution: "lz-string@npm:1.5.0" + bin: + lz-string: bin/bin.js + checksum: 10c0/36128e4de34791838abe979b19927c26e67201ca5acf00880377af7d765b38d1c60847e01c5ec61b1a260c48029084ab3893a3925fd6e48a04011364b089991b + languageName: node + linkType: hard + +"magic-string@npm:^0.30.21": + version: 0.30.21 + resolution: "magic-string@npm:0.30.21" + dependencies: + "@jridgewell/sourcemap-codec": "npm:^1.5.5" + checksum: 10c0/299378e38f9a270069fc62358522ddfb44e94244baa0d6a8980ab2a9b2490a1d03b236b447eee309e17eb3bddfa482c61259d47960eb018a904f0ded52780c4a + languageName: node + linkType: hard + "math-intrinsics@npm:^1.1.0": version: 1.1.0 resolution: "math-intrinsics@npm:1.1.0" @@ -2925,6 +3819,20 @@ __metadata: languageName: node linkType: hard +"mdn-data@npm:2.27.1": + version: 2.27.1 + resolution: "mdn-data@npm:2.27.1" + checksum: 10c0/eb8abf5d22e4d1e090346f5e81b67d23cef14c83940e445da5c44541ad874dc8fb9f6ca236e8258c3a489d9fb5884188a4d7d58773adb9089ac2c0b966796393 + languageName: node + linkType: hard + +"min-indent@npm:^1.0.0": + version: 1.0.1 + resolution: "min-indent@npm:1.0.1" + checksum: 10c0/7e207bd5c20401b292de291f02913230cb1163abca162044f7db1d951fa245b174dc00869d40dd9a9f32a885ad6a5f3e767ee104cf278f399cb4e92d3f582d5c + languageName: node + linkType: hard + "minimatch@npm:^10.2.2": version: 10.2.5 resolution: "minimatch@npm:10.2.5" @@ -2966,7 +3874,47 @@ __metadata: languageName: node linkType: hard -"nanoid@npm:^3.3.11": +"msw@npm:^2.14.6": + version: 2.14.6 + resolution: "msw@npm:2.14.6" + dependencies: + "@inquirer/confirm": "npm:^6.0.11" + "@mswjs/interceptors": "npm:^0.41.3" + "@open-draft/deferred-promise": "npm:^3.0.0" + "@types/statuses": "npm:^2.0.6" + cookie: "npm:^1.1.1" + graphql: "npm:^16.13.2" + headers-polyfill: "npm:^5.0.1" + is-node-process: "npm:^1.2.0" + outvariant: "npm:^1.4.3" + path-to-regexp: "npm:^6.3.0" + picocolors: "npm:^1.1.1" + rettime: "npm:^0.11.11" + statuses: "npm:^2.0.2" + strict-event-emitter: "npm:^0.5.1" + tough-cookie: "npm:^6.0.1" + type-fest: "npm:^5.5.0" + until-async: "npm:^3.0.2" + yargs: "npm:^17.7.2" + peerDependencies: + typescript: ">= 4.8.x" + peerDependenciesMeta: + typescript: + optional: true + bin: + msw: cli/index.js + checksum: 10c0/f6e6ca17c8e7fe4618381ce6352178a54d3b4c7abc23326db5937edf8bb91fcc259b2b6445983d30e0296ad262c01f789f2701adc166d4628d420eeafdcfdcf0 + languageName: node + linkType: hard + +"mute-stream@npm:^3.0.0": + version: 3.0.0 + resolution: "mute-stream@npm:3.0.0" + checksum: 10c0/12cdb36a101694c7a6b296632e6d93a30b74401873cf7507c88861441a090c71c77a58f213acadad03bc0c8fa186639dec99d68a14497773a8744320c136e701 + languageName: node + linkType: hard + +"nanoid@npm:^3.3.11, nanoid@npm:^3.3.12": version: 3.3.12 resolution: "nanoid@npm:3.3.12" bin: @@ -3112,7 +4060,7 @@ __metadata: languageName: node linkType: hard -"obug@npm:^2.0": +"obug@npm:^2.0, obug@npm:^2.1.1": version: 2.1.1 resolution: "obug@npm:2.1.1" checksum: 10c0/59dccd7de72a047e08f8649e94c1015ec72f94eefb6ddb57fb4812c4b425a813bc7e7cd30c9aca20db3c59abc3c85cc7a62bb656a968741d770f4e8e02bc2e78 @@ -3133,6 +4081,13 @@ __metadata: languageName: node linkType: hard +"outvariant@npm:^1.4.0, outvariant@npm:^1.4.3": + version: 1.4.3 + resolution: "outvariant@npm:1.4.3" + checksum: 10c0/5976ca7740349cb8c71bd3382e2a762b1aeca6f33dc984d9d896acdf3c61f78c3afcf1bfe9cc633a7b3c4b295ec94d292048f83ea2b2594fae4496656eba992c + languageName: node + linkType: hard + "own-keys@npm:^1.0.1": version: 1.0.1 resolution: "own-keys@npm:1.0.1" @@ -3171,6 +4126,15 @@ __metadata: languageName: node linkType: hard +"parse5@npm:^8.0.1": + version: 8.0.1 + resolution: "parse5@npm:8.0.1" + dependencies: + entities: "npm:^8.0.0" + checksum: 10c0/c3c1c5aab55f6e4be5245599790e56e64be7764a4a0edd7f98db4fe3bb380f63add752fa047dff0496446c25f4104f0c7c1967723de640bde92306a7bb67ed2f + languageName: node + linkType: hard + "path-exists@npm:^4.0.0": version: 4.0.0 resolution: "path-exists@npm:4.0.0" @@ -3192,7 +4156,21 @@ __metadata: languageName: node linkType: hard -"picocolors@npm:^1.1.1": +"path-to-regexp@npm:^6.3.0": + version: 6.3.0 + resolution: "path-to-regexp@npm:6.3.0" + checksum: 10c0/73b67f4638b41cde56254e6354e46ae3a2ebc08279583f6af3d96fe4664fc75788f74ed0d18ca44fa4a98491b69434f9eee73b97bb5314bd1b5adb700f5c18d6 + languageName: node + linkType: hard + +"pathe@npm:^2.0.3": + version: 2.0.3 + resolution: "pathe@npm:2.0.3" + checksum: 10c0/c118dc5a8b5c4166011b2b70608762e260085180bb9e33e80a50dcdb1e78c010b1624f4280c492c92b05fc276715a4c357d1f9edc570f8f1b3d90b6839ebaca1 + languageName: node + linkType: hard + +"picocolors@npm:1.1.1, picocolors@npm:^1.1.1": version: 1.1.1 resolution: "picocolors@npm:1.1.1" checksum: 10c0/e2e3e8170ab9d7c7421969adaa7e1b31434f789afb9b3f115f6b96d91945041ac3ceb02e9ec6fe6510ff036bcc0bf91e69a1772edc0b707e12b19c0f2d6bcf58 @@ -3224,6 +4202,17 @@ __metadata: languageName: node linkType: hard +"postcss@npm:^8.5.15": + version: 8.5.15 + resolution: "postcss@npm:8.5.15" + dependencies: + nanoid: "npm:^3.3.12" + picocolors: "npm:^1.1.1" + source-map-js: "npm:^1.2.1" + checksum: 10c0/7f2e63ae22fbe43aace1bf652bd99da4e90737c64194d49e51ddc9cd0f9e51ff2861a7d734379b494deffa03a880a5c65eec70bc29ee9ebaa7136dde3eee8f31 + languageName: node + linkType: hard + "prelude-ls@npm:^1.2.1": version: 1.2.1 resolution: "prelude-ls@npm:1.2.1" @@ -3240,6 +4229,17 @@ __metadata: languageName: node linkType: hard +"pretty-format@npm:^27.0.2": + version: 27.5.1 + resolution: "pretty-format@npm:27.5.1" + dependencies: + ansi-regex: "npm:^5.0.1" + ansi-styles: "npm:^5.0.0" + react-is: "npm:^17.0.1" + checksum: 10c0/0cbda1031aa30c659e10921fa94e0dd3f903ecbbbe7184a729ad66f2b6e7f17891e8c7d7654c458fa4ccb1a411ffb695b4f17bbcd3fe075fabe181027c4040ed + languageName: node + linkType: hard + "proc-log@npm:^6.0.0": version: 6.1.0 resolution: "proc-log@npm:6.1.0" @@ -3270,7 +4270,7 @@ __metadata: languageName: node linkType: hard -"punycode@npm:^2.1.0": +"punycode@npm:^2.1.0, punycode@npm:^2.3.1": version: 2.3.1 resolution: "punycode@npm:2.3.1" checksum: 10c0/14f76a8206bc3464f794fb2e3d3cc665ae416c01893ad7a02b23766eb07159144ee612ad67af5e84fa4479ccfe67678c4feb126b0485651b302babf66f04f9e9 @@ -3352,6 +4352,13 @@ __metadata: languageName: node linkType: hard +"react-is@npm:^17.0.1": + version: 17.0.2 + resolution: "react-is@npm:17.0.2" + checksum: 10c0/2bdb6b93fbb1820b024b496042cce405c57e2f85e777c9aabd55f9b26d145408f9f74f5934676ffdc46f3dcff656d78413a6e43968e7b3f92eea35b3052e9053 + languageName: node + linkType: hard + "react-lifecycles-compat@npm:^3.0.4": version: 3.0.4 resolution: "react-lifecycles-compat@npm:3.0.4" @@ -3420,6 +4427,16 @@ __metadata: languageName: node linkType: hard +"redent@npm:^3.0.0": + version: 3.0.0 + resolution: "redent@npm:3.0.0" + dependencies: + indent-string: "npm:^4.0.0" + strip-indent: "npm:^3.0.0" + checksum: 10c0/d64a6b5c0b50eb3ddce3ab770f866658a2b9998c678f797919ceb1b586bab9259b311407280bd80b804e2a7c7539b19238ae6a2a20c843f1a7fcff21d48c2eae + languageName: node + linkType: hard + "reflect.getprototypeof@npm:^1.0.6, reflect.getprototypeof@npm:^1.0.9": version: 1.0.10 resolution: "reflect.getprototypeof@npm:1.0.10" @@ -3450,6 +4467,20 @@ __metadata: languageName: node linkType: hard +"require-directory@npm:^2.1.1": + version: 2.1.1 + resolution: "require-directory@npm:2.1.1" + checksum: 10c0/83aa76a7bc1531f68d92c75a2ca2f54f1b01463cb566cf3fbc787d0de8be30c9dbc211d1d46be3497dac5785fe296f2dd11d531945ac29730643357978966e99 + languageName: node + linkType: hard + +"require-from-string@npm:^2.0.2": + version: 2.0.2 + resolution: "require-from-string@npm:2.0.2" + checksum: 10c0/aaa267e0c5b022fc5fd4eef49d8285086b15f2a1c54b28240fdf03599cbd9c26049fee3eab894f2e1f6ca65e513b030a7c264201e3f005601e80c49fb2937ce2 + languageName: node + linkType: hard + "resolve-from@npm:^4.0.0": version: 4.0.0 resolution: "resolve-from@npm:4.0.0" @@ -3489,6 +4520,13 @@ __metadata: languageName: node linkType: hard +"rettime@npm:^0.11.11": + version: 0.11.11 + resolution: "rettime@npm:0.11.11" + checksum: 10c0/021fc9d9870ce04f032952e63fc5576f3f8e7c9c15513b1a479a64646df90239802eef6d60a98cbfb6ac87bb623d4f120a8ee71193d02984e3d2915c28695f6e + languageName: node + linkType: hard + "rolldown@npm:1.0.0-rc.17": version: 1.0.0-rc.17 resolution: "rolldown@npm:1.0.0-rc.17" @@ -3547,6 +4585,64 @@ __metadata: languageName: node linkType: hard +"rolldown@npm:1.0.3": + version: 1.0.3 + resolution: "rolldown@npm:1.0.3" + dependencies: + "@oxc-project/types": "npm:=0.133.0" + "@rolldown/binding-android-arm64": "npm:1.0.3" + "@rolldown/binding-darwin-arm64": "npm:1.0.3" + "@rolldown/binding-darwin-x64": "npm:1.0.3" + "@rolldown/binding-freebsd-x64": "npm:1.0.3" + "@rolldown/binding-linux-arm-gnueabihf": "npm:1.0.3" + "@rolldown/binding-linux-arm64-gnu": "npm:1.0.3" + "@rolldown/binding-linux-arm64-musl": "npm:1.0.3" + "@rolldown/binding-linux-ppc64-gnu": "npm:1.0.3" + "@rolldown/binding-linux-s390x-gnu": "npm:1.0.3" + "@rolldown/binding-linux-x64-gnu": "npm:1.0.3" + "@rolldown/binding-linux-x64-musl": "npm:1.0.3" + "@rolldown/binding-openharmony-arm64": "npm:1.0.3" + "@rolldown/binding-wasm32-wasi": "npm:1.0.3" + "@rolldown/binding-win32-arm64-msvc": "npm:1.0.3" + "@rolldown/binding-win32-x64-msvc": "npm:1.0.3" + "@rolldown/pluginutils": "npm:^1.0.0" + dependenciesMeta: + "@rolldown/binding-android-arm64": + optional: true + "@rolldown/binding-darwin-arm64": + optional: true + "@rolldown/binding-darwin-x64": + optional: true + "@rolldown/binding-freebsd-x64": + optional: true + "@rolldown/binding-linux-arm-gnueabihf": + optional: true + "@rolldown/binding-linux-arm64-gnu": + optional: true + "@rolldown/binding-linux-arm64-musl": + optional: true + "@rolldown/binding-linux-ppc64-gnu": + optional: true + "@rolldown/binding-linux-s390x-gnu": + optional: true + "@rolldown/binding-linux-x64-gnu": + optional: true + "@rolldown/binding-linux-x64-musl": + optional: true + "@rolldown/binding-openharmony-arm64": + optional: true + "@rolldown/binding-wasm32-wasi": + optional: true + "@rolldown/binding-win32-arm64-msvc": + optional: true + "@rolldown/binding-win32-x64-msvc": + optional: true + bin: + rolldown: ./bin/cli.mjs + checksum: 10c0/5f9dd47b7abf203b16bc600db68542f245e974c800e59ff50b76157d1dada1403657690435b036fabca88e93d13a67c31abe5cfaa6f61ce33717f61720204cdf + languageName: node + linkType: hard + "safe-array-concat@npm:^1.1.3": version: 1.1.4 resolution: "safe-array-concat@npm:1.1.4" @@ -3598,6 +4694,15 @@ __metadata: languageName: node linkType: hard +"saxes@npm:^6.0.0": + version: 6.0.0 + resolution: "saxes@npm:6.0.0" + dependencies: + xmlchars: "npm:^2.2.0" + checksum: 10c0/3847b839f060ef3476eb8623d099aa502ad658f5c40fd60c105ebce86d244389b0d76fcae30f4d0c728d7705ceb2f7e9b34bb54717b6a7dbedaf5dad2d9a4b74 + languageName: node + linkType: hard + "scheduler@npm:^0.27.0": version: 0.27.0 resolution: "scheduler@npm:0.27.0" @@ -3630,6 +4735,13 @@ __metadata: languageName: node linkType: hard +"set-cookie-parser@npm:^3.0.1": + version: 3.1.0 + resolution: "set-cookie-parser@npm:3.1.0" + checksum: 10c0/7465e389ff9fb7ff243fd55f0f48b5648d53a560903db170a7f766c2b82f22f4242c4d366fcdf12afdd376496f84058025d889e718459256ecbfc9a5fe7755f5 + languageName: node + linkType: hard + "set-function-length@npm:^1.2.2": version: 1.2.2 resolution: "set-function-length@npm:1.2.2" @@ -3731,6 +4843,20 @@ __metadata: languageName: node linkType: hard +"siginfo@npm:^2.0.0": + version: 2.0.0 + resolution: "siginfo@npm:2.0.0" + checksum: 10c0/3def8f8e516fbb34cb6ae415b07ccc5d9c018d85b4b8611e3dc6f8be6d1899f693a4382913c9ed51a06babb5201639d76453ab297d1c54a456544acf5c892e34 + languageName: node + linkType: hard + +"signal-exit@npm:^4.1.0": + version: 4.1.0 + resolution: "signal-exit@npm:4.1.0" + checksum: 10c0/41602dce540e46d599edba9d9860193398d135f7ff72cab629db5171516cfae628d21e7bfccde1bbfdf11c48726bc2a6d1a8fb8701125852fbfda7cf19c6aa83 + languageName: node + linkType: hard + "source-map-js@npm:>=0.6.2 <2.0.0, source-map-js@npm:^1.2.1": version: 1.2.1 resolution: "source-map-js@npm:1.2.1" @@ -3738,6 +4864,27 @@ __metadata: languageName: node linkType: hard +"stackback@npm:0.0.2": + version: 0.0.2 + resolution: "stackback@npm:0.0.2" + checksum: 10c0/89a1416668f950236dd5ac9f9a6b2588e1b9b62b1b6ad8dff1bfc5d1a15dbf0aafc9b52d2226d00c28dffff212da464eaeebfc6b7578b9d180cef3e3782c5983 + languageName: node + linkType: hard + +"statuses@npm:^2.0.2": + version: 2.0.2 + resolution: "statuses@npm:2.0.2" + checksum: 10c0/a9947d98ad60d01f6b26727570f3bcceb6c8fa789da64fe6889908fe2e294d57503b14bf2b5af7605c2d36647259e856635cd4c49eab41667658ec9d0080ec3f + languageName: node + linkType: hard + +"std-env@npm:^4.0.0-rc.1": + version: 4.1.0 + resolution: "std-env@npm:4.1.0" + checksum: 10c0/2e14b6b490db34cb969a48d9cf7c35bca4a47653914aac2814221baae7b867a5b15940d133625c391621971f98cd2266a5dc7036669960e883f1081db2a56558 + languageName: node + linkType: hard + "stop-iteration-iterator@npm:^1.1.0": version: 1.1.0 resolution: "stop-iteration-iterator@npm:1.1.0" @@ -3748,6 +4895,24 @@ __metadata: languageName: node linkType: hard +"strict-event-emitter@npm:^0.5.1": + version: 0.5.1 + resolution: "strict-event-emitter@npm:0.5.1" + checksum: 10c0/f5228a6e6b6393c57f52f62e673cfe3be3294b35d6f7842fc24b172ae0a6e6c209fa83241d0e433fc267c503bc2f4ffdbe41a9990ff8ffd5ac425ec0489417f7 + languageName: node + linkType: hard + +"string-width@npm:^4.1.0, string-width@npm:^4.2.0, string-width@npm:^4.2.3": + version: 4.2.3 + resolution: "string-width@npm:4.2.3" + dependencies: + emoji-regex: "npm:^8.0.0" + is-fullwidth-code-point: "npm:^3.0.0" + strip-ansi: "npm:^6.0.1" + checksum: 10c0/1e525e92e5eae0afd7454086eed9c818ee84374bb80328fc41217ae72ff5f065ef1c9d7f72da41de40c75fa8bb3dee63d92373fd492c84260a552c636392a47b + languageName: node + linkType: hard + "string.prototype.matchall@npm:^4.0.12": version: 4.0.12 resolution: "string.prototype.matchall@npm:4.0.12" @@ -3817,6 +4982,24 @@ __metadata: languageName: node linkType: hard +"strip-ansi@npm:^6.0.0, strip-ansi@npm:^6.0.1": + version: 6.0.1 + resolution: "strip-ansi@npm:6.0.1" + dependencies: + ansi-regex: "npm:^5.0.1" + checksum: 10c0/1ae5f212a126fe5b167707f716942490e3933085a5ff6c008ab97ab2f272c8025d3aa218b7bd6ab25729ca20cc81cddb252102f8751e13482a5199e873680952 + languageName: node + linkType: hard + +"strip-indent@npm:^3.0.0": + version: 3.0.0 + resolution: "strip-indent@npm:3.0.0" + dependencies: + min-indent: "npm:^1.0.0" + checksum: 10c0/ae0deaf41c8d1001c5d4fbe16cb553865c1863da4fae036683b474fa926af9fc121e155cb3fc57a68262b2ae7d5b8420aa752c97a6428c315d00efe2a3875679 + languageName: node + linkType: hard + "strip-json-comments@npm:^3.1.1": version: 3.1.1 resolution: "strip-json-comments@npm:3.1.1" @@ -3840,6 +5023,20 @@ __metadata: languageName: node linkType: hard +"symbol-tree@npm:^3.2.4": + version: 3.2.4 + resolution: "symbol-tree@npm:3.2.4" + checksum: 10c0/dfbe201ae09ac6053d163578778c53aa860a784147ecf95705de0cd23f42c851e1be7889241495e95c37cabb058edb1052f141387bef68f705afc8f9dd358509 + languageName: node + linkType: hard + +"tagged-tag@npm:^1.0.0": + version: 1.0.0 + resolution: "tagged-tag@npm:1.0.0" + checksum: 10c0/91d25c9ffb86a91f20522cefb2cbec9b64caa1febe27ad0df52f08993ff60888022d771e868e6416cf2e72dab68449d2139e8709ba009b74c6c7ecd4000048d1 + languageName: node + linkType: hard + "tar@npm:^7.5.4": version: 7.5.13 resolution: "tar@npm:7.5.13" @@ -3853,6 +5050,20 @@ __metadata: languageName: node linkType: hard +"tinybench@npm:^2.9.0": + version: 2.9.0 + resolution: "tinybench@npm:2.9.0" + checksum: 10c0/c3500b0f60d2eb8db65250afe750b66d51623057ee88720b7f064894a6cb7eb93360ca824a60a31ab16dab30c7b1f06efe0795b352e37914a9d4bad86386a20c + languageName: node + linkType: hard + +"tinyexec@npm:^1.0.2": + version: 1.2.4 + resolution: "tinyexec@npm:1.2.4" + checksum: 10c0/153b8db6b080194b558ff145b9cffc36b80a6e07babd644dcfbe49c807eee668c876049d28bdee90b96304476f883352f2dad91b3f86bc23832532f4363e66ff + languageName: node + linkType: hard + "tinyglobby@npm:^0.2.12, tinyglobby@npm:^0.2.15, tinyglobby@npm:^0.2.16": version: 0.2.16 resolution: "tinyglobby@npm:0.2.16" @@ -3863,6 +5074,59 @@ __metadata: languageName: node linkType: hard +"tinyglobby@npm:^0.2.17": + version: 0.2.17 + resolution: "tinyglobby@npm:0.2.17" + dependencies: + fdir: "npm:^6.5.0" + picomatch: "npm:^4.0.4" + checksum: 10c0/7f7bb0f197c88bc4b20c231e0deca4240ca3bf313a88f5a7fee93a872b84966a4d50220947c0455ad07a60b3b360961c5b7fd979222aeb716a9f99b412002e4c + languageName: node + linkType: hard + +"tinyrainbow@npm:^3.1.0": + version: 3.1.0 + resolution: "tinyrainbow@npm:3.1.0" + checksum: 10c0/f11cf387a26c5c9255bec141a90ac511b26172981b10c3e50053bc6700ea7d2336edcc4a3a21dbb8412fe7c013477d2ba4d7e4877800f3f8107be5105aad6511 + languageName: node + linkType: hard + +"tldts-core@npm:^7.4.2": + version: 7.4.2 + resolution: "tldts-core@npm:7.4.2" + checksum: 10c0/e8e02a43f6823ea1beab8f5a9da370b9a6cbf1a942d4f7d828700e65f03a119348f8d19faa95b599f3ca76fcb5fe5c4611d5b9c274f5a58c7487d342f6083a06 + languageName: node + linkType: hard + +"tldts@npm:^7.0.5": + version: 7.4.2 + resolution: "tldts@npm:7.4.2" + dependencies: + tldts-core: "npm:^7.4.2" + bin: + tldts: bin/cli.js + checksum: 10c0/68f7ec58c9ea41f1583a6384f0fdf1b33d2d8ee55e7d403ec5cf9de4a7226a25c585b5ce1a73de8ddc9abbbe7b783bb3554d1c811fd47fceb0d5511e06d0d766 + languageName: node + linkType: hard + +"tough-cookie@npm:^6.0.1": + version: 6.0.1 + resolution: "tough-cookie@npm:6.0.1" + dependencies: + tldts: "npm:^7.0.5" + checksum: 10c0/ec70bd6b1215efe4ed31a158f0be3e4c9088fcbd8620edc23a5860d4f3d85c757b77e274baaa700f7b25e409f4181552ed189603c2b2e1a9f88104da3a61a37d + languageName: node + linkType: hard + +"tr46@npm:^6.0.0": + version: 6.0.0 + resolution: "tr46@npm:6.0.0" + dependencies: + punycode: "npm:^2.3.1" + checksum: 10c0/83130df2f649228aa91c17754b66248030a3af34911d713b5ea417066fa338aa4bc8668d06bd98aa21a2210f43fc0a3db8b9099e7747fb5830e40e39a6a1058e + languageName: node + linkType: hard + "ts-api-utils@npm:^2.5.0": version: 2.5.0 resolution: "ts-api-utils@npm:2.5.0" @@ -3888,6 +5152,15 @@ __metadata: languageName: node linkType: hard +"type-fest@npm:^5.5.0": + version: 5.7.0 + resolution: "type-fest@npm:5.7.0" + dependencies: + tagged-tag: "npm:^1.0.0" + checksum: 10c0/f71ed17b753649421e419db8cc2e140f930333a1467b1d9cca2e0e4052900fd442f2360bae73f3a6bf9340d949ac46d9a1598c709b4c8089272e7624df9c8716 + languageName: node + linkType: hard + "typed-array-buffer@npm:^1.0.3": version: 1.0.3 resolution: "typed-array-buffer@npm:1.0.3" @@ -4011,6 +5284,13 @@ __metadata: languageName: node linkType: hard +"undici-types@npm:>=7.24.0 <7.24.7": + version: 7.24.6 + resolution: "undici-types@npm:7.24.6" + checksum: 10c0/d9cd8befb643ac904615c280a095ba4240531f6bb4a5e75a22a7483630ca8d3f1016d2ab6ace6ceda1f63b3a2db2fe037fafe121d6917a0187573aa548ff78ca + languageName: node + linkType: hard + "undici-types@npm:~7.19.0": version: 7.19.2 resolution: "undici-types@npm:7.19.2" @@ -4025,6 +5305,20 @@ __metadata: languageName: node linkType: hard +"undici@npm:^7.25.0": + version: 7.27.0 + resolution: "undici@npm:7.27.0" + checksum: 10c0/6fd15a81b0ca177b2667738b830ed175363e5e2164e992251d03aaee6c6517098b930085bd68b8fe5911920371076526657de035e07dc72377b9c5c731b90f0b + languageName: node + linkType: hard + +"until-async@npm:^3.0.2": + version: 3.0.2 + resolution: "until-async@npm:3.0.2" + checksum: 10c0/61c8b03895dbe18fe3d90316d0a1894e0c131ea4b1673f6ce78eed993d0bb81bbf4b7adf8477e9ff7725782a76767eed9d077561cfc9f89b4a1ebe61f7c9828e + languageName: node + linkType: hard + "update-browserslist-db@npm:^1.2.3": version: 1.2.3 resolution: "update-browserslist-db@npm:1.2.3" @@ -4069,6 +5363,63 @@ __metadata: languageName: node linkType: hard +"vite@npm:^6.0.0 || ^7.0.0 || ^8.0.0": + version: 8.0.16 + resolution: "vite@npm:8.0.16" + dependencies: + fsevents: "npm:~2.3.3" + lightningcss: "npm:^1.32.0" + picomatch: "npm:^4.0.4" + postcss: "npm:^8.5.15" + rolldown: "npm:1.0.3" + tinyglobby: "npm:^0.2.17" + peerDependencies: + "@types/node": ^20.19.0 || >=22.12.0 + "@vitejs/devtools": ^0.1.18 + esbuild: ^0.27.0 || ^0.28.0 + jiti: ">=1.21.0" + less: ^4.0.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 + dependenciesMeta: + fsevents: + optional: true + peerDependenciesMeta: + "@types/node": + optional: true + "@vitejs/devtools": + optional: true + esbuild: + optional: true + jiti: + optional: true + less: + optional: true + sass: + optional: true + sass-embedded: + optional: true + stylus: + optional: true + sugarss: + optional: true + terser: + optional: true + tsx: + optional: true + yaml: + optional: true + bin: + vite: bin/vite.js + checksum: 10c0/d75be3fbe2f63e6a8145325970338afaf0dd4d96ba9175c13f9a286fd5f95afc489401b693e4fa6c0899a4dd0e137be91cdf9401a40a635563911ad5036e3467 + languageName: node + linkType: hard + "vite@npm:^8.0.0": version: 8.0.10 resolution: "vite@npm:8.0.10" @@ -4126,6 +5477,83 @@ __metadata: languageName: node linkType: hard +"vitest@npm:^4.1.8": + version: 4.1.8 + resolution: "vitest@npm:4.1.8" + dependencies: + "@vitest/expect": "npm:4.1.8" + "@vitest/mocker": "npm:4.1.8" + "@vitest/pretty-format": "npm:4.1.8" + "@vitest/runner": "npm:4.1.8" + "@vitest/snapshot": "npm:4.1.8" + "@vitest/spy": "npm:4.1.8" + "@vitest/utils": "npm:4.1.8" + es-module-lexer: "npm:^2.0.0" + expect-type: "npm:^1.3.0" + magic-string: "npm:^0.30.21" + obug: "npm:^2.1.1" + pathe: "npm:^2.0.3" + picomatch: "npm:^4.0.3" + std-env: "npm:^4.0.0-rc.1" + tinybench: "npm:^2.9.0" + tinyexec: "npm:^1.0.2" + tinyglobby: "npm:^0.2.15" + tinyrainbow: "npm:^3.1.0" + vite: "npm:^6.0.0 || ^7.0.0 || ^8.0.0" + why-is-node-running: "npm:^2.3.0" + peerDependencies: + "@edge-runtime/vm": "*" + "@opentelemetry/api": ^1.9.0 + "@types/node": ^20.0.0 || ^22.0.0 || >=24.0.0 + "@vitest/browser-playwright": 4.1.8 + "@vitest/browser-preview": 4.1.8 + "@vitest/browser-webdriverio": 4.1.8 + "@vitest/coverage-istanbul": 4.1.8 + "@vitest/coverage-v8": 4.1.8 + "@vitest/ui": 4.1.8 + happy-dom: "*" + jsdom: "*" + vite: ^6.0.0 || ^7.0.0 || ^8.0.0 + peerDependenciesMeta: + "@edge-runtime/vm": + optional: true + "@opentelemetry/api": + optional: true + "@types/node": + optional: true + "@vitest/browser-playwright": + optional: true + "@vitest/browser-preview": + optional: true + "@vitest/browser-webdriverio": + optional: true + "@vitest/coverage-istanbul": + optional: true + "@vitest/coverage-v8": + optional: true + "@vitest/ui": + optional: true + happy-dom: + optional: true + jsdom: + optional: true + vite: + optional: false + bin: + vitest: vitest.mjs + checksum: 10c0/f459c500f8818c7a2318cd23228b4e5c0b5efb25bf8e5cb7f116c6d26e51482b2f800a8bb19837c0b5f0d05c51519edbf502bc8ceb5bd86868e8facf1d2c498e + languageName: node + linkType: hard + +"w3c-xmlserializer@npm:^5.0.0": + version: 5.0.0 + resolution: "w3c-xmlserializer@npm:5.0.0" + dependencies: + xml-name-validator: "npm:^5.0.0" + checksum: 10c0/8712774c1aeb62dec22928bf1cdfd11426c2c9383a1a63f2bcae18db87ca574165a0fbe96b312b73652149167ac6c7f4cf5409f2eb101d9c805efe0e4bae798b + languageName: node + linkType: hard + "warning@npm:^4.0.0, warning@npm:^4.0.3": version: 4.0.3 resolution: "warning@npm:4.0.3" @@ -4135,6 +5563,31 @@ __metadata: languageName: node linkType: hard +"webidl-conversions@npm:^8.0.1": + version: 8.0.1 + resolution: "webidl-conversions@npm:8.0.1" + checksum: 10c0/3f6f327ca5fa0c065ed8ed0ef3b72f33623376e68f958e9b7bd0df49fdb0b908139ac2338d19fb45bd0e05595bda96cb6d1622222a8b413daa38a17aacc4dd46 + languageName: node + linkType: hard + +"whatwg-mimetype@npm:^5.0.0": + version: 5.0.0 + resolution: "whatwg-mimetype@npm:5.0.0" + checksum: 10c0/eead164fe73a00dd82f817af6fc0bd22e9c273e1d55bf4bc6bdf2da7ad8127fca82ef00ea6a37892f5f5641f8e34128e09508f92126086baba126b9e0d57feb4 + languageName: node + linkType: hard + +"whatwg-url@npm:^16.0.0, whatwg-url@npm:^16.0.1": + version: 16.0.1 + resolution: "whatwg-url@npm:16.0.1" + dependencies: + "@exodus/bytes": "npm:^1.11.0" + tr46: "npm:^6.0.0" + webidl-conversions: "npm:^8.0.1" + checksum: 10c0/e75565566abf3a2cdbd9f06c965dbcccee6ec4e9f0d3728ad5e08ceb9944279848bcaa211d35a29cb6d2df1e467dd05cfb59fbddf8a0adcd7d0bce9ffb703fd2 + languageName: node + linkType: hard + "which-boxed-primitive@npm:^1.1.0, which-boxed-primitive@npm:^1.1.1": version: 1.1.1 resolution: "which-boxed-primitive@npm:1.1.1" @@ -4218,6 +5671,18 @@ __metadata: languageName: node linkType: hard +"why-is-node-running@npm:^2.3.0": + version: 2.3.0 + resolution: "why-is-node-running@npm:2.3.0" + dependencies: + siginfo: "npm:^2.0.0" + stackback: "npm:0.0.2" + bin: + why-is-node-running: cli.js + checksum: 10c0/1cde0b01b827d2cf4cb11db962f3958b9175d5d9e7ac7361d1a7b0e2dc6069a263e69118bd974c4f6d0a890ef4eedfe34cf3d5167ec14203dbc9a18620537054 + languageName: node + linkType: hard + "word-wrap@npm:^1.2.5": version: 1.2.5 resolution: "word-wrap@npm:1.2.5" @@ -4225,6 +5690,38 @@ __metadata: languageName: node linkType: hard +"wrap-ansi@npm:^7.0.0": + version: 7.0.0 + resolution: "wrap-ansi@npm:7.0.0" + dependencies: + ansi-styles: "npm:^4.0.0" + string-width: "npm:^4.1.0" + strip-ansi: "npm:^6.0.0" + checksum: 10c0/d15fc12c11e4cbc4044a552129ebc75ee3f57aa9c1958373a4db0292d72282f54373b536103987a4a7594db1ef6a4f10acf92978f79b98c49306a4b58c77d4da + languageName: node + linkType: hard + +"xml-name-validator@npm:^5.0.0": + version: 5.0.0 + resolution: "xml-name-validator@npm:5.0.0" + checksum: 10c0/3fcf44e7b73fb18be917fdd4ccffff3639373c7cb83f8fc35df6001fecba7942f1dbead29d91ebb8315e2f2ff786b508f0c9dc0215b6353f9983c6b7d62cb1f5 + languageName: node + linkType: hard + +"xmlchars@npm:^2.2.0": + version: 2.2.0 + resolution: "xmlchars@npm:2.2.0" + checksum: 10c0/b64b535861a6f310c5d9bfa10834cf49127c71922c297da9d4d1b45eeaae40bf9b4363275876088fbe2667e5db028d2cd4f8ee72eed9bede840a67d57dab7593 + languageName: node + linkType: hard + +"y18n@npm:^5.0.5": + version: 5.0.8 + resolution: "y18n@npm:5.0.8" + checksum: 10c0/4df2842c36e468590c3691c894bc9cdbac41f520566e76e24f59401ba7d8b4811eb1e34524d57e54bc6d864bcb66baab7ffd9ca42bf1eda596618f9162b91249 + languageName: node + linkType: hard + "yallist@npm:^3.0.2": version: 3.1.1 resolution: "yallist@npm:3.1.1" @@ -4239,6 +5736,28 @@ __metadata: languageName: node linkType: hard +"yargs-parser@npm:^21.1.1": + version: 21.1.1 + resolution: "yargs-parser@npm:21.1.1" + checksum: 10c0/f84b5e48169479d2f402239c59f084cfd1c3acc197a05c59b98bab067452e6b3ea46d4dd8ba2985ba7b3d32a343d77df0debd6b343e5dae3da2aab2cdf5886b2 + languageName: node + linkType: hard + +"yargs@npm:^17.7.2": + version: 17.7.2 + resolution: "yargs@npm:17.7.2" + dependencies: + cliui: "npm:^8.0.1" + escalade: "npm:^3.1.1" + get-caller-file: "npm:^2.0.5" + require-directory: "npm:^2.1.1" + string-width: "npm:^4.2.3" + y18n: "npm:^5.0.5" + yargs-parser: "npm:^21.1.1" + checksum: 10c0/ccd7e723e61ad5965fffbb791366db689572b80cca80e0f96aad968dfff4156cd7cd1ad18607afe1046d8241e6fb2d6c08bf7fa7bfb5eaec818735d8feac8f05 + languageName: node + linkType: hard + "yocto-queue@npm:^0.1.0": version: 0.1.0 resolution: "yocto-queue@npm:0.1.0"