From b4efb8b7e64196fded9ea98ff3708561677bbd26 Mon Sep 17 00:00:00 2001 From: Dagonite Date: Tue, 14 Apr 2026 08:37:41 +0100 Subject: [PATCH 1/8] Update experiment viewer interface --- src/components/experimentViewer/Graph.tsx | 144 ++++++++++++++++++---- 1 file changed, 122 insertions(+), 22 deletions(-) diff --git a/src/components/experimentViewer/Graph.tsx b/src/components/experimentViewer/Graph.tsx index d91ea223..868bfe5b 100644 --- a/src/components/experimentViewer/Graph.tsx +++ b/src/components/experimentViewer/Graph.tsx @@ -1,8 +1,38 @@ -import React, { useState } from 'react'; +import React, { useMemo, useState } from 'react'; import ndarray from 'ndarray'; -import { getDomain, LineVis, ScaleType, ScaleSelector, Separator, ToggleBtn, Toolbar } from '@h5web/lib'; +import { + CurveType, + DomainWidget, + getDomain, + Interpolation, + LineVis, + Menu, + RadioGroup, + ScaleSelector, + ScaleType, + Separator, + ToggleBtn, + Toolbar, + useSafeDomain, +} from '@h5web/lib'; +import type { AxisScaleType, CustomDomain, Domain } from '@h5web/lib'; import type { LinePlotData } from '../../lib/types'; -import { Box, Paper, Typography, useTheme } from '@mui/material'; +import { Box, Paper, Typography } from '@mui/material'; +import { alpha, useTheme } from '@mui/material/styles'; + +const DEFAULT_DOMAIN: Domain = [0.1, 1]; +const AXIS_SCALE_OPTIONS: AxisScaleType[] = [ScaleType.Linear, ScaleType.Log, ScaleType.SymLog]; + +const CURVE_TYPE_LABELS: Record = { + [CurveType.LineOnly]: 'Line', + [CurveType.GlyphsOnly]: 'Glyphs', + [CurveType.LineAndGlyphs]: 'Line + Glyphs', +}; + +const INTERPOLATION_LABELS: Record = { + [Interpolation.Linear]: 'Linear', + [Interpolation.Constant]: 'Constant', +}; interface PlotViewerProps { linePlotData: LinePlotData[]; @@ -15,8 +45,11 @@ const PlotViewer: React.FC = ({ linePlotData, showErrors, onSho // State for line plot controls const [lineShowGrid, setLineShowGrid] = useState(true); - const [xScaleType, setXScaleType] = useState(ScaleType.Linear); - const [yScaleType, setYScaleType] = useState(ScaleType.Linear); + const [xScaleType, setXScaleType] = useState(ScaleType.Linear); + const [yScaleType, setYScaleType] = useState(ScaleType.Linear); + const [customYDomain, setCustomYDomain] = useState([null, null]); + const [curveType, setCurveType] = useState(CurveType.LineOnly); + const [interpolation, setInterpolation] = useState(Interpolation.Linear); // Handle empty state if (linePlotData.length === 0) { @@ -51,9 +84,6 @@ const PlotViewer: React.FC = ({ linePlotData, showErrors, onSho const primaryArray = ndarray(primaryData.data, [primaryData.data.length]); - // Generate abscissas (x-values) for primary data based on its length - const primaryAbscissas = Float32Array.from({ length: primaryData.data.length }, (_, i) => i); - // Create error array if available and showErrors is true const primaryErrorsArray = showErrors && primaryData.errors ? ndarray(primaryData.errors, [primaryData.errors.length]) : undefined; @@ -99,26 +129,74 @@ const PlotViewer: React.FC = ({ linePlotData, showErrors, onSho } } - const domain = combinedDomain; + const autoDomain = combinedDomain || DEFAULT_DOMAIN; + const effectiveYDomain = useMemo( + () => [customYDomain[0] ?? autoDomain[0], customYDomain[1] ?? autoDomain[1]], + [autoDomain, customYDomain] + ); + const [safeYDomain] = useSafeDomain(effectiveYDomain, autoDomain, yScaleType); + + const h5WebThemeTokens = { + color: theme.palette.text.primary, + backgroundColor: theme.palette.background.paper, + '--h5w-btn-hover--bgColor': theme.palette.action.hover, + '--h5w-btn-hover--shadowColor': alpha(theme.palette.text.primary, theme.palette.mode === 'dark' ? 0.28 : 0.16), + '--h5w-btnRaised--bgColor': theme.palette.background.default, + '--h5w-btnRaised--shadowColor': alpha(theme.palette.text.primary, theme.palette.mode === 'dark' ? 0.3 : 0.18), + '--h5w-btnRaised-hover--shadowColor': alpha( + theme.palette.text.primary, + theme.palette.mode === 'dark' ? 0.42 : 0.24 + ), + '--h5w-btnPressed--bgColor': alpha(theme.palette.primary.main, theme.palette.mode === 'dark' ? 0.32 : 0.18), + '--h5w-btnPressed--shadowColor': alpha(theme.palette.primary.main, theme.palette.mode === 'dark' ? 0.5 : 0.32), + '--h5w-btnPressed-hover--shadowColor': alpha( + theme.palette.primary.main, + theme.palette.mode === 'dark' ? 0.62 : 0.4 + ), + '--h5w-toolbar--bgColor': theme.palette.background.paper, + '--h5w-toolbar-label--color': theme.palette.text.secondary, + '--h5w-toolbar-separator--color': alpha(theme.palette.text.primary, theme.palette.mode === 'dark' ? 0.2 : 0.14), + '--h5w-toolbar-popup--bgColor': theme.palette.background.paper, + '--h5w-toolbar-input-focus--shadowColor': theme.palette.primary.main, + '--h5w-selector-arrowIcon--color': theme.palette.text.secondary, + '--h5w-selector-label--color': theme.palette.text.secondary, + '--h5w-selector-groupLabel--color': theme.palette.text.secondary, + '--h5w-selector-menu--bgColor': theme.palette.background.paper, + '--h5w-selector-option-hover--bgColor': theme.palette.action.hover, + '--h5w-selector-option-selected--bgColor': alpha( + theme.palette.primary.main, + theme.palette.mode === 'dark' ? 0.3 : 0.16 + ), + '--h5w-selector-option-focus--outlineColor': theme.palette.primary.main, + '--h5w-domainWidget-popup--bgColor': theme.palette.background.paper, + '--h5w-domainControls--colorAlt': theme.palette.text.primary, + '--h5w-domainControls-boundInput--shadowColor': alpha( + theme.palette.text.primary, + theme.palette.mode === 'dark' ? 0.3 : 0.16 + ), + '--h5w-domainControls-boundInput-focus--shadowColor': theme.palette.primary.main, + '--h5w-domainControls-boundInput-editing--bgColor': theme.palette.background.default, + '--h5w-domainControls-boundInput-editing--borderColor': theme.palette.primary.main, + '--h5w-error--color': theme.palette.error.main, + }; return ( - + + + {/* Y-axis scale selector */} @@ -126,25 +204,47 @@ const PlotViewer: React.FC = ({ linePlotData, showErrors, onSho setLineShowGrid(!lineShowGrid)} /> onShowErrorsChange(!showErrors)} /> + + + + + 0 ? auxiliaries : undefined} showGrid={lineShowGrid} scaleType={yScaleType} - abscissaParams={{ scaleType: xScaleType, value: primaryAbscissas }} + curveType={curveType} + interpolation={interpolation} + abscissaParams={{ label: 'Index', scaleType: xScaleType }} /> ); From 39956b174a32800bbd53ff6086a7e041dc6cb262 Mon Sep 17 00:00:00 2001 From: Dagonite Date: Tue, 14 Apr 2026 08:38:14 +0100 Subject: [PATCH 2/8] Add jest tests --- .../experimentViewer/Graph.test.tsx | 251 ++++++++++++++++++ 1 file changed, 251 insertions(+) create mode 100644 src/components/experimentViewer/Graph.test.tsx diff --git a/src/components/experimentViewer/Graph.test.tsx b/src/components/experimentViewer/Graph.test.tsx new file mode 100644 index 00000000..241f1453 --- /dev/null +++ b/src/components/experimentViewer/Graph.test.tsx @@ -0,0 +1,251 @@ +import React, { act } from 'react'; +import { ThemeProvider, createTheme } from '@mui/material/styles'; +import { createRoot, type Root } from 'react-dom/client'; + +import type { LinePlotData } from '../../lib/types'; +import PlotViewer from './Graph'; + +type MockLineVisProps = { + abscissaParams?: { + label?: string; + scaleType?: string; + }; + curveType?: string; + domain?: [number, number]; + interpolation?: string; +}; + +const mockLineVis = jest.fn(); + +jest.mock('@h5web/lib', () => { + const React = require('react') as typeof import('react'); + const mockBuildDomain = ( + array: { data: ArrayLike }, + _scaleType?: string, + errors?: { data: ArrayLike } + ): [number, number] | undefined => { + let min = Number.POSITIVE_INFINITY; + let max = Number.NEGATIVE_INFINITY; + + Array.from(array.data).forEach((value, index) => { + if (!Number.isFinite(value)) { + return; + } + + const error = errors ? Array.from(errors.data)[index] : undefined; + const low = Number.isFinite(error) ? value - error : value; + const high = Number.isFinite(error) ? value + error : value; + + min = Math.min(min, low); + max = Math.max(max, high); + }); + + return Number.isFinite(min) && Number.isFinite(max) ? [min, max] : undefined; + }; + + const ScaleType = { + Linear: 'linear', + Log: 'log', + SymLog: 'symlog', + }; + + const CurveType = { + LineOnly: 'OnlyLine', + GlyphsOnly: 'OnlyGlyphs', + LineAndGlyphs: 'LineAndGlyphs', + }; + + const Interpolation = { + Linear: 'Linear', + Constant: 'Constant', + }; + + return { + CurveType, + DomainWidget: ({ + onCustomDomainChange, + }: { + onCustomDomainChange: (domain: [number | null, number | null]) => void; + }) => ( +
+ + +
+ ), + getDomain: mockBuildDomain, + Interpolation, + LineVis: (props: MockLineVisProps) => { + mockLineVis(props); + return
; + }, + Menu: ({ label, children }: { label: string; children: React.ReactNode }) => ( +
+ {label} + {children} +
+ ), + RadioGroup: ({ + label, + options, + optionsLabels, + value, + onChange, + }: { + label?: string; + options: string[]; + optionsLabels?: Record; + value: string; + onChange: (nextValue: string) => void; + }) => ( +
+ {label && {label}} + {options.map((option) => ( + + ))} +
+ ), + ScaleSelector: ({ + label, + value, + options, + onScaleChange, + }: { + label: string; + value: string; + options: string[]; + onScaleChange: (nextValue: string) => void; + }) => ( + + ), + ScaleType, + Separator: () => , + ToggleBtn: ({ label, value, onToggle }: { label: string; value?: boolean; onToggle: () => void }) => ( + + ), + Toolbar: ({ children }: { children: React.ReactNode }) =>
{children}
, + useSafeDomain: (domain: [number, number]) => [domain], + }; +}); + +(globalThis as typeof globalThis & { IS_REACT_ACT_ENVIRONMENT?: boolean }).IS_REACT_ACT_ENVIRONMENT = true; + +const linePlotData: LinePlotData[] = [ + { + filename: 'primary-file', + data: [1, 2, 4], + errors: [0.1, 0.2, 0.3], + }, + { + filename: 'secondary-file', + data: [1.5, 3], + errors: [0.05, 0.15], + }, +]; + +let mountedContainer: HTMLDivElement | null = null; +let mountedRoot: Root | null = null; + +function renderPlotViewer(showErrors = false, onShowErrorsChange = jest.fn()) { + mountedContainer = document.createElement('div'); + document.body.appendChild(mountedContainer); + mountedRoot = createRoot(mountedContainer); + + act(() => { + mountedRoot!.render( + + + + ); + }); + + return mountedContainer; +} + +function getLastLineVisProps(): MockLineVisProps { + return mockLineVis.mock.calls[mockLineVis.mock.calls.length - 1][0] as MockLineVisProps; +} + +function getButton(container: HTMLElement, matcher: string | RegExp): HTMLButtonElement { + const button = Array.from(container.querySelectorAll('button')).find((candidate) => { + const label = candidate.textContent || ''; + return typeof matcher === 'string' ? label === matcher : matcher.test(label); + }); + + if (!button) { + throw new Error(`Button not found: ${matcher.toString()}`); + } + + return button; +} + +function clickButton(container: HTMLElement, matcher: string | RegExp): void { + act(() => { + getButton(container, matcher).dispatchEvent(new MouseEvent('click', { bubbles: true })); + }); +} + +beforeEach(() => { + mockLineVis.mockClear(); +}); + +afterEach(() => { + if (mountedRoot) { + act(() => { + mountedRoot!.unmount(); + }); + } + + mountedRoot = null; + mountedContainer?.remove(); + mountedContainer = null; +}); + +test('renders the extended 1D toolbar and labels the x axis as Index', () => { + const container = renderPlotViewer(); + + expect(container.textContent).toContain('Aspect'); + expect(container.textContent).toContain('Curve type'); + expect(container.textContent).toContain('Interpolation'); + expect(getLastLineVisProps().abscissaParams).toEqual( + expect.objectContaining({ + label: 'Index', + scaleType: 'linear', + }) + ); +}); + +test('updates LineVis props when the y range and style controls change', () => { + const onShowErrorsChange = jest.fn(); + + const container = renderPlotViewer(false, onShowErrorsChange); + + expect(getLastLineVisProps().domain).toEqual([1, 4]); + + clickButton(container, 'Set y min'); + expect(getLastLineVisProps().domain).toEqual([2.5, 4]); + + clickButton(container, 'Line + Glyphs'); + expect(getLastLineVisProps().curveType).toBe('LineAndGlyphs'); + + clickButton(container, 'Constant'); + expect(getLastLineVisProps().interpolation).toBe('Constant'); + + clickButton(container, /X scale:/i); + expect(getLastLineVisProps().abscissaParams?.scaleType).toBe('log'); + + clickButton(container, 'Error Bars'); + expect(onShowErrorsChange).toHaveBeenCalledWith(true); + + clickButton(container, 'Reset y range'); + expect(getLastLineVisProps().domain).toEqual([1, 4]); +}); From 8206986dfe50cf231dbd7cfa7f3db4c6e60f30c5 Mon Sep 17 00:00:00 2001 From: Dagonite Date: Thu, 16 Apr 2026 08:46:24 +0100 Subject: [PATCH 3/8] Improve viewer UI and empty-state handling --- .../experimentViewer/ExperimentSearch.tsx | 21 +++- src/components/experimentViewer/FileCard.tsx | 2 +- .../experimentViewer/Graph.test.tsx | 6 +- src/components/experimentViewer/Graph.tsx | 114 +++++++++--------- .../experimentViewer/ViewerTabs.tsx | 93 +++++++++++++- 5 files changed, 170 insertions(+), 66 deletions(-) diff --git a/src/components/experimentViewer/ExperimentSearch.tsx b/src/components/experimentViewer/ExperimentSearch.tsx index 49534eac..f39a3d7a 100644 --- a/src/components/experimentViewer/ExperimentSearch.tsx +++ b/src/components/experimentViewer/ExperimentSearch.tsx @@ -9,6 +9,7 @@ import { ToggleButtonGroup, ToggleButton, } from '@mui/material'; +import { alpha, useTheme } from '@mui/material/styles'; import SearchIcon from '@mui/icons-material/Search'; import ClearIcon from '@mui/icons-material/Clear'; import { instruments } from '../../lib/instrumentData'; @@ -30,6 +31,7 @@ const ExperimentSearch: React.FC = ({ isLoading = false, isSearchActive = false, }): JSX.Element => { + const theme = useTheme(); const [selectedInstrument, setSelectedInstrument] = useState(initialInstrument || null); const [experimentNumber, setExperimentNumber] = useState( initialExperimentNumber ? initialExperimentNumber.toString() : '' @@ -74,7 +76,7 @@ const ExperimentSearch: React.FC = ({ /> setExperimentNumber(e.target.value)} @@ -102,7 +104,22 @@ const ExperimentSearch: React.FC = ({ startIcon={} onClick={handleClear} disabled={isLoading} - sx={{ height: 40 }} + sx={{ + height: 40, + color: theme.palette.mode === 'dark' ? theme.palette.common.white : theme.palette.text.primary, + borderColor: + theme.palette.mode === 'dark' + ? alpha(theme.palette.common.white, 0.28) + : alpha(theme.palette.text.primary, 0.18), + bgcolor: + theme.palette.mode === 'dark' + ? alpha(theme.palette.common.white, 0.06) + : alpha(theme.palette.background.paper, 0.95), + '&:hover': { + borderColor: theme.palette.primary.main, + bgcolor: alpha(theme.palette.primary.main, theme.palette.mode === 'dark' ? 0.18 : 0.08), + }, + }} > Clear diff --git a/src/components/experimentViewer/FileCard.tsx b/src/components/experimentViewer/FileCard.tsx index 57380d89..256343cb 100644 --- a/src/components/experimentViewer/FileCard.tsx +++ b/src/components/experimentViewer/FileCard.tsx @@ -196,7 +196,7 @@ const FileCard: React.FC = ({ {/* Mode toggle */} - Slice Selection (2D → 1D) + Slice selection (2D → 1D) { - const React = require('react') as typeof import('react'); + const React = jest.requireActual('react') as typeof import('react'); const mockBuildDomain = ( array: { data: ArrayLike }, _scaleType?: string, @@ -155,7 +155,7 @@ const linePlotData: LinePlotData[] = [ let mountedContainer: HTMLDivElement | null = null; let mountedRoot: Root | null = null; -function renderPlotViewer(showErrors = false, onShowErrorsChange = jest.fn()) { +function renderPlotViewer(showErrors = false, onShowErrorsChange = jest.fn()): HTMLDivElement { mountedContainer = document.createElement('div'); document.body.appendChild(mountedContainer); mountedRoot = createRoot(mountedContainer); @@ -243,7 +243,7 @@ test('updates LineVis props when the y range and style controls change', () => { clickButton(container, /X scale:/i); expect(getLastLineVisProps().abscissaParams?.scaleType).toBe('log'); - clickButton(container, 'Error Bars'); + clickButton(container, 'Error bars'); expect(onShowErrorsChange).toHaveBeenCalledWith(true); clickButton(container, 'Reset y range'); diff --git a/src/components/experimentViewer/Graph.tsx b/src/components/experimentViewer/Graph.tsx index 868bfe5b..1f79b8ee 100644 --- a/src/components/experimentViewer/Graph.tsx +++ b/src/components/experimentViewer/Graph.tsx @@ -42,6 +42,7 @@ interface PlotViewerProps { const PlotViewer: React.FC = ({ linePlotData, showErrors, onShowErrorsChange }): JSX.Element => { const theme = useTheme(); + const hasData = linePlotData.length > 0; // State for line plot controls const [lineShowGrid, setLineShowGrid] = useState(true); @@ -51,75 +52,52 @@ const PlotViewer: React.FC = ({ linePlotData, showErrors, onSho const [curveType, setCurveType] = useState(CurveType.LineOnly); const [interpolation, setInterpolation] = useState(Interpolation.Linear); - // Handle empty state - if (linePlotData.length === 0) { - return ( - - - - No data selected - - - Select files from the left panel to plot - - - - ); - } - // Sort data by domain size (largest first) to ensure the file with the biggest domain is primary // This prevents crashes when auxiliary data has a larger domain than primary - const sortedData = [...linePlotData].sort((a, b) => b.data.length - a.data.length); + const sortedData = hasData ? [...linePlotData].sort((a, b) => b.data.length - a.data.length) : []; // Render line plots - largest domain as primary, rest as auxiliaries const primaryData = sortedData[0]; - - const primaryArray = ndarray(primaryData.data, [primaryData.data.length]); + const primaryLength = primaryData?.data.length ?? DEFAULT_DOMAIN.length; + const primaryArray = ndarray(primaryData?.data ?? DEFAULT_DOMAIN, [primaryLength]); // Create error array if available and showErrors is true const primaryErrorsArray = - showErrors && primaryData.errors ? ndarray(primaryData.errors, [primaryData.errors.length]) : undefined; + showErrors && primaryData?.errors ? ndarray(primaryData.errors, [primaryData.errors.length]) : undefined; // Create auxiliaries for additional lines with error bars // Pad auxiliary arrays with NaN to match primary length (NaN values won't render) - const primaryLength = primaryData.data.length; - const auxiliaries = sortedData.slice(1).map((data) => { - // Pad data array with NaN if shorter than primary - const paddedData = new Float32Array(primaryLength); - paddedData.set(data.data); - if (data.data.length < primaryLength) { - paddedData.fill(NaN, data.data.length); - } - - // Pad errors array with NaN if available and shorter than primary - let paddedErrors: Float32Array | undefined; - if (showErrors && data.errors) { - paddedErrors = new Float32Array(primaryLength); - paddedErrors.set(data.errors); - if (data.errors.length < primaryLength) { - paddedErrors.fill(NaN, data.errors.length); - } - } - - return { - array: ndarray(paddedData, [primaryLength]), - label: data.filename, - color: data.color, - errors: paddedErrors ? ndarray(paddedErrors, [primaryLength]) : undefined, - }; - }); + const auxiliaries = primaryData + ? sortedData.slice(1).map((data) => { + // Pad data array with NaN if shorter than primary + const paddedData = new Float32Array(primaryLength); + paddedData.set(data.data); + if (data.data.length < primaryLength) { + paddedData.fill(NaN, data.data.length); + } + + // Pad errors array with NaN if available and shorter than primary + let paddedErrors: Float32Array | undefined; + if (showErrors && data.errors) { + paddedErrors = new Float32Array(primaryLength); + paddedErrors.set(data.errors); + if (data.errors.length < primaryLength) { + paddedErrors.fill(NaN, data.errors.length); + } + } + + return { + array: ndarray(paddedData, [primaryLength]), + label: data.filename, + color: data.color, + errors: paddedErrors ? ndarray(paddedErrors, [primaryLength]) : undefined, + }; + }) + : []; // Calculate combined Y domain across all data to ensure proper graph sizing // Start with primary data domain - let combinedDomain = getDomain(primaryArray, yScaleType, primaryErrorsArray); + let combinedDomain = primaryData ? getDomain(primaryArray, yScaleType, primaryErrorsArray) : DEFAULT_DOMAIN; // Extend domain to include all auxiliaries for (const aux of auxiliaries) { @@ -136,6 +114,30 @@ const PlotViewer: React.FC = ({ linePlotData, showErrors, onSho ); const [safeYDomain] = useSafeDomain(effectiveYDomain, autoDomain, yScaleType); + // Handle empty state + if (!primaryData) { + return ( + + + + No data selected + + + Select files from the left panel to plot + + + + ); + } + const h5WebThemeTokens = { color: theme.palette.text.primary, backgroundColor: theme.palette.background.paper, @@ -210,7 +212,7 @@ const PlotViewer: React.FC = ({ linePlotData, showErrors, onSho setLineShowGrid(!lineShowGrid)} /> - onShowErrorsChange(!showErrors)} /> + onShowErrorsChange(!showErrors)} /> = ({ activeTab, onTabChange }): JSX.Element => { + const theme = useTheme(); + return ( - - onTabChange(newTab)} variant="fullWidth"> - } iconPosition="start" /> - } iconPosition="start" /> + + onTabChange(newTab)} + variant="fullWidth" + sx={{ + minHeight: 0, + p: 0.5, + borderRadius: 2, + bgcolor: alpha(theme.palette.text.primary, theme.palette.mode === 'dark' ? 0.12 : 0.05), + '& .MuiTabs-flexContainer': { + gap: 1, + }, + '& .MuiTabs-indicator': { + display: 'none', + }, + }} + > + } + iconPosition="start" + sx={{ + minHeight: 42, + borderRadius: 1.5, + textTransform: 'none', + fontWeight: 600, + color: 'text.secondary', + transition: theme.transitions.create(['background-color', 'color', 'box-shadow'], { + duration: theme.transitions.duration.shorter, + }), + '& .MuiSvgIcon-root': { + color: 'inherit', + }, + '&:hover': { + bgcolor: alpha(theme.palette.text.primary, theme.palette.mode === 'dark' ? 0.1 : 0.06), + }, + '&.Mui-selected': { + color: theme.palette.primary.contrastText, + bgcolor: theme.palette.primary.main, + boxShadow: `0 0 0 1px ${alpha(theme.palette.primary.main, 0.24)}, 0 6px 16px ${alpha( + theme.palette.primary.main, + theme.palette.mode === 'dark' ? 0.4 : 0.18 + )}`, + }, + }} + /> + } + iconPosition="start" + sx={{ + minHeight: 42, + borderRadius: 1.5, + textTransform: 'none', + fontWeight: 600, + color: 'text.secondary', + transition: theme.transitions.create(['background-color', 'color', 'box-shadow'], { + duration: theme.transitions.duration.shorter, + }), + '& .MuiSvgIcon-root': { + color: 'inherit', + }, + '&:hover': { + bgcolor: alpha(theme.palette.text.primary, theme.palette.mode === 'dark' ? 0.1 : 0.06), + }, + '&.Mui-selected': { + color: theme.palette.primary.contrastText, + bgcolor: theme.palette.primary.main, + boxShadow: `0 0 0 1px ${alpha(theme.palette.primary.main, 0.24)}, 0 6px 16px ${alpha( + theme.palette.primary.main, + theme.palette.mode === 'dark' ? 0.4 : 0.18 + )}`, + }, + }} + /> ); From e162e7e884e74e61deaceac3483d8fea96cfed38 Mon Sep 17 00:00:00 2001 From: Dagonite Date: Thu, 16 Apr 2026 09:17:31 +0100 Subject: [PATCH 4/8] Refine experiment viewer UI text and styles --- .../experimentViewer/ExperimentSearch.tsx | 23 +++---- src/components/experimentViewer/FileTree.tsx | 2 +- src/components/experimentViewer/Graph.tsx | 66 +++++++++++++++---- src/components/experimentViewer/Viewer2D.tsx | 2 +- .../experimentViewer/ViewerTabs.tsx | 4 +- src/pages/ExperimentViewer.tsx | 4 +- 6 files changed, 70 insertions(+), 31 deletions(-) diff --git a/src/components/experimentViewer/ExperimentSearch.tsx b/src/components/experimentViewer/ExperimentSearch.tsx index f39a3d7a..69d09cc5 100644 --- a/src/components/experimentViewer/ExperimentSearch.tsx +++ b/src/components/experimentViewer/ExperimentSearch.tsx @@ -57,7 +57,6 @@ const ExperimentSearch: React.FC = ({ // Get instrument names for autocomplete const instrumentNames = instruments.map((instrument) => instrument.name); - return ( @@ -138,6 +137,18 @@ const ExperimentSearch: React.FC = ({ onChange={(_, newLimit) => newLimit !== null && setResultLimit(newLimit)} size="small" disabled={isLoading} + sx={{ + borderRadius: 0, + '& .MuiToggleButtonGroup-grouped': { + borderRadius: 0, + width: 56, + minWidth: 56, + height: 36, + px: 0, + justifyContent: 'center', + fontVariantNumeric: 'tabular-nums', + }, + }} > 10 25 @@ -145,16 +156,6 @@ const ExperimentSearch: React.FC = ({ 100 - - {isSearchActive && (selectedInstrument || experimentNumber) && ( - - - Searching for: {selectedInstrument && Instrument: {selectedInstrument}} - {selectedInstrument && experimentNumber && ' | '} - {experimentNumber && Experiment: {experimentNumber}} - - - )} ); }; diff --git a/src/components/experimentViewer/FileTree.tsx b/src/components/experimentViewer/FileTree.tsx index b2c274ff..2cf35f46 100644 --- a/src/components/experimentViewer/FileTree.tsx +++ b/src/components/experimentViewer/FileTree.tsx @@ -100,7 +100,7 @@ const FileTree: React.FC = ({ > - File Tree + File tree {/* Settings */} diff --git a/src/components/experimentViewer/Graph.tsx b/src/components/experimentViewer/Graph.tsx index 1f79b8ee..44fc6b01 100644 --- a/src/components/experimentViewer/Graph.tsx +++ b/src/components/experimentViewer/Graph.tsx @@ -19,6 +19,8 @@ import type { AxisScaleType, CustomDomain, Domain } from '@h5web/lib'; import type { LinePlotData } from '../../lib/types'; import { Box, Paper, Typography } from '@mui/material'; import { alpha, useTheme } from '@mui/material/styles'; +import resetZoomButtonStyles from '../../h5web/packages/lib/src/toolbar/floating/ResetZoomButton.module.css'; +import tooltipStyles from '../../h5web/packages/lib/src/vis/shared/Tooltip.module.css'; const DEFAULT_DOMAIN: Domain = [0.1, 1]; const AXIS_SCALE_OPTIONS: AxisScaleType[] = [ScaleType.Linear, ScaleType.Log, ScaleType.SymLog]; @@ -138,7 +140,16 @@ const PlotViewer: React.FC = ({ linePlotData, showErrors, onSho ); } - const h5WebThemeTokens = { + const tooltipBackground = + theme.palette.mode === 'dark' ? alpha(theme.palette.grey[900], 0.94) : alpha(theme.palette.background.paper, 0.97); + const tooltipBorderColor = + theme.palette.mode === 'dark' ? alpha(theme.palette.common.white, 0.12) : alpha(theme.palette.text.primary, 0.12); + const floatingControlBackground = + theme.palette.mode === 'dark' ? alpha(theme.palette.grey[900], 0.82) : alpha(theme.palette.background.paper, 0.84); + const floatingControlBorderColor = + theme.palette.mode === 'dark' ? alpha(theme.palette.common.white, 0.16) : alpha(theme.palette.text.primary, 0.12); + + const toolbarThemeTokens = { color: theme.palette.text.primary, backgroundColor: theme.palette.background.paper, '--h5w-btn-hover--bgColor': theme.palette.action.hover, @@ -182,9 +193,34 @@ const PlotViewer: React.FC = ({ linePlotData, showErrors, onSho '--h5w-error--color': theme.palette.error.main, }; + const lineVisStyles = { + '--h5w-tooltip-guide--color': alpha(theme.palette.primary.main, theme.palette.mode === 'dark' ? 0.82 : 0.6), + '--h5w-tooltip-guide--opacity': theme.palette.mode === 'dark' ? 0.9 : 0.72, + [`& .${tooltipStyles.tooltip}`]: { + backgroundColor: tooltipBackground, + color: theme.palette.text.primary, + border: `1px solid ${tooltipBorderColor}`, + boxShadow: `0 0 0 1px ${tooltipBorderColor}, 0 12px 28px ${alpha( + theme.palette.common.black, + theme.palette.mode === 'dark' ? 0.5 : 0.16 + )}`, + backdropFilter: 'blur(8px)', + }, + [`& .${resetZoomButtonStyles.btnLike}`]: { + color: theme.palette.text.primary, + backgroundColor: floatingControlBackground, + border: `1px solid ${floatingControlBorderColor}`, + boxShadow: `0 0 0 1px ${floatingControlBorderColor}, 0 10px 24px ${alpha( + theme.palette.common.black, + theme.palette.mode === 'dark' ? 0.42 : 0.14 + )}`, + backdropFilter: 'blur(8px)', + }, + }; + return ( - + = ({ linePlotData, showErrors, onSho - 0 ? auxiliaries : undefined} - showGrid={lineShowGrid} - scaleType={yScaleType} - curveType={curveType} - interpolation={interpolation} - abscissaParams={{ label: 'Index', scaleType: xScaleType }} - /> + + 0 ? auxiliaries : undefined} + showGrid={lineShowGrid} + scaleType={yScaleType} + curveType={curveType} + interpolation={interpolation} + abscissaParams={{ label: 'Index', scaleType: xScaleType }} + /> + ); }; diff --git a/src/components/experimentViewer/Viewer2D.tsx b/src/components/experimentViewer/Viewer2D.tsx index e6686e9c..0c70cafd 100644 --- a/src/components/experimentViewer/Viewer2D.tsx +++ b/src/components/experimentViewer/Viewer2D.tsx @@ -39,7 +39,7 @@ const Viewer2D: React.FC = ({ filepath }): JSX.Element => { Select a file to view 2D data - Choose a file from the File Tree to visualize HDF5 datasets in 2D + Choose a file from the File tree to visualize HDF5 datasets in 2D diff --git a/src/components/experimentViewer/ViewerTabs.tsx b/src/components/experimentViewer/ViewerTabs.tsx index de4ed729..f14fba52 100644 --- a/src/components/experimentViewer/ViewerTabs.tsx +++ b/src/components/experimentViewer/ViewerTabs.tsx @@ -41,7 +41,7 @@ const ViewerTabs: React.FC = ({ activeTab, onTabChange }): JSX. > } iconPosition="start" sx={{ @@ -71,7 +71,7 @@ const ViewerTabs: React.FC = ({ activeTab, onTabChange }): JSX. /> } iconPosition="start" sx={{ diff --git a/src/pages/ExperimentViewer.tsx b/src/pages/ExperimentViewer.tsx index 6b286244..ac09c417 100644 --- a/src/pages/ExperimentViewer.tsx +++ b/src/pages/ExperimentViewer.tsx @@ -520,7 +520,7 @@ const ExperimentViewer: React.FC = (): JSX.Element => { > - Search for HDF5 Data + Search for HDF5 data Enter an instrument and/or experiment number above to search for jobs with HDF5 output files. @@ -541,7 +541,7 @@ const ExperimentViewer: React.FC = (): JSX.Element => { > - No Jobs Found + No jobs found {searchInstrument && `Instrument: ${searchInstrument}`} From 25c7e62c074d1291fd9a80ff4d3f8f24e2d0a025 Mon Sep 17 00:00:00 2001 From: Dagonite Date: Thu, 16 Apr 2026 09:37:23 +0100 Subject: [PATCH 5/8] Refine graph toolbar UI, labels and icons --- .../experimentViewer/Graph.test.tsx | 2 +- src/components/experimentViewer/Graph.tsx | 47 +++++++++++++++---- src/pages/ExperimentViewer.tsx | 2 +- 3 files changed, 41 insertions(+), 10 deletions(-) diff --git a/src/components/experimentViewer/Graph.test.tsx b/src/components/experimentViewer/Graph.test.tsx index 03e1731e..8070860e 100644 --- a/src/components/experimentViewer/Graph.test.tsx +++ b/src/components/experimentViewer/Graph.test.tsx @@ -234,7 +234,7 @@ test('updates LineVis props when the y range and style controls change', () => { clickButton(container, 'Set y min'); expect(getLastLineVisProps().domain).toEqual([2.5, 4]); - clickButton(container, 'Line + Glyphs'); + clickButton(container, 'Line + Points'); expect(getLastLineVisProps().curveType).toBe('LineAndGlyphs'); clickButton(container, 'Constant'); diff --git a/src/components/experimentViewer/Graph.tsx b/src/components/experimentViewer/Graph.tsx index 44fc6b01..bfa39d18 100644 --- a/src/components/experimentViewer/Graph.tsx +++ b/src/components/experimentViewer/Graph.tsx @@ -19,6 +19,8 @@ import type { AxisScaleType, CustomDomain, Domain } from '@h5web/lib'; import type { LinePlotData } from '../../lib/types'; import { Box, Paper, Typography } from '@mui/material'; import { alpha, useTheme } from '@mui/material/styles'; +import { MdAutoGraph, MdGridOn } from 'react-icons/md'; +import ErrorsIcon from '../../h5web/packages/app/src/vis-packs/core/line/ErrorsIcon'; import resetZoomButtonStyles from '../../h5web/packages/lib/src/toolbar/floating/ResetZoomButton.module.css'; import tooltipStyles from '../../h5web/packages/lib/src/vis/shared/Tooltip.module.css'; @@ -27,8 +29,8 @@ const AXIS_SCALE_OPTIONS: AxisScaleType[] = [ScaleType.Linear, ScaleType.Log, Sc const CURVE_TYPE_LABELS: Record = { [CurveType.LineOnly]: 'Line', - [CurveType.GlyphsOnly]: 'Glyphs', - [CurveType.LineAndGlyphs]: 'Line + Glyphs', + [CurveType.GlyphsOnly]: 'Points', + [CurveType.LineAndGlyphs]: 'Line + Points', }; const INTERPOLATION_LABELS: Record = { @@ -145,9 +147,15 @@ const PlotViewer: React.FC = ({ linePlotData, showErrors, onSho const tooltipBorderColor = theme.palette.mode === 'dark' ? alpha(theme.palette.common.white, 0.12) : alpha(theme.palette.text.primary, 0.12); const floatingControlBackground = - theme.palette.mode === 'dark' ? alpha(theme.palette.grey[900], 0.82) : alpha(theme.palette.background.paper, 0.84); + theme.palette.mode === 'dark' ? alpha(theme.palette.grey[800], 0.96) : alpha(theme.palette.background.paper, 0.9); + const floatingControlHoverBackground = + theme.palette.mode === 'dark' ? alpha(theme.palette.grey[700], 0.98) : alpha(theme.palette.background.paper, 0.98); + const floatingControlTextColor = + theme.palette.mode === 'dark' ? theme.palette.common.white : theme.palette.text.primary; const floatingControlBorderColor = - theme.palette.mode === 'dark' ? alpha(theme.palette.common.white, 0.16) : alpha(theme.palette.text.primary, 0.12); + theme.palette.mode === 'dark' ? alpha(theme.palette.common.white, 0.24) : alpha(theme.palette.text.primary, 0.12); + const floatingControlHoverBorderColor = + theme.palette.mode === 'dark' ? alpha(theme.palette.common.white, 0.34) : alpha(theme.palette.text.primary, 0.18); const toolbarThemeTokens = { color: theme.palette.text.primary, @@ -207,7 +215,7 @@ const PlotViewer: React.FC = ({ linePlotData, showErrors, onSho backdropFilter: 'blur(8px)', }, [`& .${resetZoomButtonStyles.btnLike}`]: { - color: theme.palette.text.primary, + color: floatingControlTextColor, backgroundColor: floatingControlBackground, border: `1px solid ${floatingControlBorderColor}`, boxShadow: `0 0 0 1px ${floatingControlBorderColor}, 0 10px 24px ${alpha( @@ -215,6 +223,19 @@ const PlotViewer: React.FC = ({ linePlotData, showErrors, onSho theme.palette.mode === 'dark' ? 0.42 : 0.14 )}`, backdropFilter: 'blur(8px)', + fontWeight: 500, + }, + [`& .${resetZoomButtonStyles.btn}:hover > .${resetZoomButtonStyles.btnLike}, & .${resetZoomButtonStyles.btn}:focus-visible > .${resetZoomButtonStyles.btnLike}`]: + { + backgroundColor: floatingControlHoverBackground, + borderColor: floatingControlHoverBorderColor, + boxShadow: `0 0 0 1px ${floatingControlHoverBorderColor}, 0 12px 28px ${alpha( + theme.palette.common.black, + theme.palette.mode === 'dark' ? 0.5 : 0.16 + )}`, + }, + [`& .${resetZoomButtonStyles.btn}:focus-visible`]: { + outline: 'none', }, }; @@ -246,11 +267,21 @@ const PlotViewer: React.FC = ({ linePlotData, showErrors, onSho label="X scale" /> - setLineShowGrid(!lineShowGrid)} /> + setLineShowGrid(!lineShowGrid)} + /> - onShowErrorsChange(!showErrors)} /> + onShowErrorsChange(!showErrors)} + /> - + { {/* Results - show FileTree and Graph when we have jobs */} {(jobId || instrumentName || (isSearchActive && jobs.length > 0)) && ( <> - {/* Left panel - File Tree */} + {/* Left panel - File tree */} Date: Thu, 16 Apr 2026 09:55:17 +0100 Subject: [PATCH 6/8] Remove input box placeholders --- src/components/experimentViewer/ExperimentSearch.tsx | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/components/experimentViewer/ExperimentSearch.tsx b/src/components/experimentViewer/ExperimentSearch.tsx index 69d09cc5..46fa31c7 100644 --- a/src/components/experimentViewer/ExperimentSearch.tsx +++ b/src/components/experimentViewer/ExperimentSearch.tsx @@ -70,7 +70,7 @@ const ExperimentSearch: React.FC = ({ options={instrumentNames} sx={{ width: 200 }} size="small" - renderInput={(params) => } + renderInput={(params) => } disabled={isLoading} /> @@ -83,7 +83,6 @@ const ExperimentSearch: React.FC = ({ type="number" size="small" sx={{ width: 175 }} - placeholder="Enter number" disabled={isLoading} /> From 6b8700fdb0068ec06541bd278c579ab231f1f22d Mon Sep 17 00:00:00 2001 From: Dagonite Date: Thu, 16 Apr 2026 10:07:37 +0100 Subject: [PATCH 7/8] Fix Graph test typings by separating Jest and Cypress TS configs --- cypress/tsconfig.json | 7 +++++++ src/components/experimentViewer/Graph.test.tsx | 6 ++++-- tsconfig.json | 3 ++- 3 files changed, 13 insertions(+), 3 deletions(-) create mode 100644 cypress/tsconfig.json diff --git a/cypress/tsconfig.json b/cypress/tsconfig.json new file mode 100644 index 00000000..875f0ab5 --- /dev/null +++ b/cypress/tsconfig.json @@ -0,0 +1,7 @@ +{ + "extends": "../tsconfig.json", + "compilerOptions": { + "types": ["cypress", "node"] + }, + "include": ["./**/*.ts", "./**/*.tsx"] +} diff --git a/src/components/experimentViewer/Graph.test.tsx b/src/components/experimentViewer/Graph.test.tsx index 8070860e..3251c5b7 100644 --- a/src/components/experimentViewer/Graph.test.tsx +++ b/src/components/experimentViewer/Graph.test.tsx @@ -1,4 +1,5 @@ import React, { act } from 'react'; +import { afterEach, beforeEach, expect, jest, test } from '@jest/globals'; import { ThemeProvider, createTheme } from '@mui/material/styles'; import { createRoot, type Root } from 'react-dom/client'; @@ -33,8 +34,9 @@ jest.mock('@h5web/lib', () => { } const error = errors ? Array.from(errors.data)[index] : undefined; - const low = Number.isFinite(error) ? value - error : value; - const high = Number.isFinite(error) ? value + error : value; + const hasFiniteError = typeof error === 'number' && Number.isFinite(error); + const low = hasFiniteError ? value - error : value; + const high = hasFiniteError ? value + error : value; min = Math.min(min, low); max = Math.max(max, high); diff --git a/tsconfig.json b/tsconfig.json index 94691a7d..4d85d019 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -2,6 +2,7 @@ "compilerOptions": { "target": "es5", "lib": ["dom", "dom.iterable", "esnext"], + "types": ["jest", "node"], "allowJs": true, "skipLibCheck": true, "esModuleInterop": true, @@ -22,5 +23,5 @@ "@h5web/lib": ["src/h5web/packages/lib/src/index.ts"] } }, - "include": ["src", "cypress/component/Homepage.cy.tsx"] + "include": ["src"] } From 8b7bfccb5cc5ee4b9a45b2f0f11dcf678238c1c7 Mon Sep 17 00:00:00 2001 From: Dagonite Date: Thu, 16 Apr 2026 10:33:28 +0100 Subject: [PATCH 8/8] Move Graph test workflow from Jest to Vitest --- package.json | 17 +- .../experimentViewer/Graph.test.tsx | 15 +- tsconfig.json | 2 +- vitest.config.ts | 32 + yarn.lock | 884 +++++++++++++++++- 5 files changed, 919 insertions(+), 31 deletions(-) create mode 100644 vitest.config.ts diff --git a/package.json b/package.json index a3bffbb3..7314510e 100644 --- a/package.json +++ b/package.json @@ -74,8 +74,8 @@ "build": "vite build", "build:standalone": "cross-env BUNDLE_REACT=true vite build", "preview": "vite preview --port 5001", - "test": "react-scripts test --testPathIgnorePatterns=src/h5web/ --env=jsdom --coverage --watchAll=false", - "test:watch": "react-scripts test --env=jsdom --watch", + "test": "vitest run --coverage", + "test:watch": "vitest", "serve:build": "yarn build && vite preview --port 5001", "serve:backend": "node server/server.js", "analyze": "yarn build && source-map-explorer build/main.*", @@ -100,15 +100,6 @@ "not ie <= 11", "not op_mini all" ], - "jest": { - "collectCoverageFrom": [ - "src/**/*.{tsx,js,jsx}", - "!src/index.tsx", - "!src/serviceWorker.ts", - "!src/setupTests.js", - "!src/testbed/**/*" - ] - }, "devDependencies": { "@cypress/react": "^7.0.3", "@cypress/webpack-dev-server": "^3.11.0", @@ -116,6 +107,7 @@ "@typescript-eslint/eslint-plugin": "8.16.0", "@typescript-eslint/parser": "8.16.0", "@vitejs/plugin-react": "^4.3.4", + "@vitest/coverage-v8": "^3.2.4", "cross-env": "^7.0.3", "cypress": "^13.16.0", "cypress-real-events": "^1.13.0", @@ -129,7 +121,8 @@ "lint-staged": "15.2.10", "prettier": "3.4.1", "serve": "14.2.4", - "vite": "^6.4.2" + "vite": "^6.4.2", + "vitest": "^3.2.4" }, "packageManager": "yarn@1.22.22+sha512.a6b2f7906b721bba3d67d4aff083df04dad64c399707841b7acf00f6b133b7ac24255f2652fa22ae3534329dc6180534e98d17432037ff6fd140556e2bb3137e" } diff --git a/src/components/experimentViewer/Graph.test.tsx b/src/components/experimentViewer/Graph.test.tsx index 3251c5b7..a97c845a 100644 --- a/src/components/experimentViewer/Graph.test.tsx +++ b/src/components/experimentViewer/Graph.test.tsx @@ -1,5 +1,5 @@ import React, { act } from 'react'; -import { afterEach, beforeEach, expect, jest, test } from '@jest/globals'; +import { afterEach, beforeEach, expect, test, vi } from 'vitest'; import { ThemeProvider, createTheme } from '@mui/material/styles'; import { createRoot, type Root } from 'react-dom/client'; @@ -16,10 +16,9 @@ type MockLineVisProps = { interpolation?: string; }; -const mockLineVis = jest.fn(); +const mockLineVis = vi.fn(); -jest.mock('@h5web/lib', () => { - const React = jest.requireActual('react') as typeof import('react'); +vi.mock('@h5web/lib', () => { const mockBuildDomain = ( array: { data: ArrayLike }, _scaleType?: string, @@ -84,7 +83,7 @@ jest.mock('@h5web/lib', () => { mockLineVis(props); return
; }, - Menu: ({ label, children }: { label: string; children: React.ReactNode }) => ( + Menu: ({ label, children }: { label: string; children: import('react').ReactNode }) => (
{label} {children} @@ -134,7 +133,7 @@ jest.mock('@h5web/lib', () => { {label} ), - Toolbar: ({ children }: { children: React.ReactNode }) =>
{children}
, + Toolbar: ({ children }: { children: import('react').ReactNode }) =>
{children}
, useSafeDomain: (domain: [number, number]) => [domain], }; }); @@ -157,7 +156,7 @@ const linePlotData: LinePlotData[] = [ let mountedContainer: HTMLDivElement | null = null; let mountedRoot: Root | null = null; -function renderPlotViewer(showErrors = false, onShowErrorsChange = jest.fn()): HTMLDivElement { +function renderPlotViewer(showErrors = false, onShowErrorsChange = vi.fn()): HTMLDivElement { mountedContainer = document.createElement('div'); document.body.appendChild(mountedContainer); mountedRoot = createRoot(mountedContainer); @@ -227,7 +226,7 @@ test('renders the extended 1D toolbar and labels the x axis as Index', () => { }); test('updates LineVis props when the y range and style controls change', () => { - const onShowErrorsChange = jest.fn(); + const onShowErrorsChange = vi.fn(); const container = renderPlotViewer(false, onShowErrorsChange); diff --git a/tsconfig.json b/tsconfig.json index 4d85d019..e098525c 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -2,7 +2,7 @@ "compilerOptions": { "target": "es5", "lib": ["dom", "dom.iterable", "esnext"], - "types": ["jest", "node"], + "types": ["node"], "allowJs": true, "skipLibCheck": true, "esModuleInterop": true, diff --git a/vitest.config.ts b/vitest.config.ts new file mode 100644 index 00000000..ca0d1149 --- /dev/null +++ b/vitest.config.ts @@ -0,0 +1,32 @@ +import { defineConfig, mergeConfig } from 'vitest/config'; + +import viteConfig from './vite.config'; + +export default mergeConfig( + viteConfig, + defineConfig({ + define: { + 'process.env.NODE_ENV': JSON.stringify('test'), + }, + test: { + environment: 'jsdom', + include: ['src/**/*.test.{ts,tsx,js,jsx}'], + exclude: ['src/h5web/**', 'cypress/**', 'node_modules/**'], + setupFiles: ['./src/setupTests.js'], + coverage: { + provider: 'v8', + reporter: ['text', 'lcov'], + include: ['src/**/*.{ts,tsx,js,jsx}'], + exclude: [ + 'src/**/*.test.{ts,tsx,js,jsx}', + 'src/h5web/**', + 'src/index.tsx', + 'src/serviceWorker.ts', + 'src/setupTests.js', + 'src/setupTests.ts', + 'src/testbed/**/*', + ], + }, + }, + }) +); diff --git a/yarn.lock b/yarn.lock index 848eb48d..add4a442 100644 --- a/yarn.lock +++ b/yarn.lock @@ -12,7 +12,7 @@ __metadata: languageName: node linkType: hard -"@ampproject/remapping@npm:^2.2.0": +"@ampproject/remapping@npm:^2.2.0, @ampproject/remapping@npm:^2.3.0": version: 2.3.0 resolution: "@ampproject/remapping@npm:2.3.0" dependencies: @@ -407,6 +407,13 @@ __metadata: languageName: node linkType: hard +"@babel/helper-validator-identifier@npm:^7.28.5": + version: 7.28.5 + resolution: "@babel/helper-validator-identifier@npm:7.28.5" + checksum: 5a251a6848e9712aea0338f659a1a3bd334d26219d5511164544ca8ec20774f098c3a6661e9da65a0d085c745c00bb62c8fada38a62f08fa1f8053bc0aeb57e4 + languageName: node + linkType: hard + "@babel/helper-validator-option@npm:^7.25.9": version: 7.25.9 resolution: "@babel/helper-validator-option@npm:7.25.9" @@ -463,6 +470,17 @@ __metadata: languageName: node linkType: hard +"@babel/parser@npm:^7.25.4": + version: 7.29.2 + resolution: "@babel/parser@npm:7.29.2" + dependencies: + "@babel/types": ^7.29.0 + bin: + parser: ./bin/babel-parser.js + checksum: 25249623ffceb61beda0ba67776cf3957ffd49bef3005ccb81da3049db52115c91ad97c97da661b714f92d062e052d07bd2ba6cba6b5460f168ff38dabaf4d6d + languageName: node + linkType: hard + "@babel/parser@npm:^7.27.2": version: 7.27.5 resolution: "@babel/parser@npm:7.27.5" @@ -1793,6 +1811,16 @@ __metadata: languageName: node linkType: hard +"@babel/types@npm:^7.25.4, @babel/types@npm:^7.29.0": + version: 7.29.0 + resolution: "@babel/types@npm:7.29.0" + dependencies: + "@babel/helper-string-parser": ^7.27.1 + "@babel/helper-validator-identifier": ^7.28.5 + checksum: 83f190438e94c22b2574aaeef7501830311ef266eaabfb06523409f64e2fe855e522951607085d71cad286719adef14e1ba37b671f334a7cd25b0f8506a01e0b + languageName: node + linkType: hard + "@babel/types@npm:^7.27.1, @babel/types@npm:^7.27.3, @babel/types@npm:^7.27.6": version: 7.27.6 resolution: "@babel/types@npm:7.27.6" @@ -1820,6 +1848,13 @@ __metadata: languageName: node linkType: hard +"@bcoe/v8-coverage@npm:^1.0.2": + version: 1.0.2 + resolution: "@bcoe/v8-coverage@npm:1.0.2" + checksum: f4e6f55817645fc1b543aa0bbd6ffceb7b9ff3052e8c92c493a0a71831e6b8ae97d73e123b048cb98ef9d9e31afae018a60795f2e27a6f3e94a1ec7abedac85d + languageName: node + linkType: hard + "@colors/colors@npm:1.5.0": version: 1.5.0 resolution: "@colors/colors@npm:1.5.0" @@ -2238,6 +2273,13 @@ __metadata: languageName: node linkType: hard +"@esbuild/aix-ppc64@npm:0.27.7": + version: 0.27.7 + resolution: "@esbuild/aix-ppc64@npm:0.27.7" + conditions: os=aix & cpu=ppc64 + languageName: node + linkType: hard + "@esbuild/android-arm64@npm:0.25.12": version: 0.25.12 resolution: "@esbuild/android-arm64@npm:0.25.12" @@ -2245,6 +2287,13 @@ __metadata: languageName: node linkType: hard +"@esbuild/android-arm64@npm:0.27.7": + version: 0.27.7 + resolution: "@esbuild/android-arm64@npm:0.27.7" + conditions: os=android & cpu=arm64 + languageName: node + linkType: hard + "@esbuild/android-arm@npm:0.25.12": version: 0.25.12 resolution: "@esbuild/android-arm@npm:0.25.12" @@ -2252,6 +2301,13 @@ __metadata: languageName: node linkType: hard +"@esbuild/android-arm@npm:0.27.7": + version: 0.27.7 + resolution: "@esbuild/android-arm@npm:0.27.7" + conditions: os=android & cpu=arm + languageName: node + linkType: hard + "@esbuild/android-x64@npm:0.25.12": version: 0.25.12 resolution: "@esbuild/android-x64@npm:0.25.12" @@ -2259,6 +2315,13 @@ __metadata: languageName: node linkType: hard +"@esbuild/android-x64@npm:0.27.7": + version: 0.27.7 + resolution: "@esbuild/android-x64@npm:0.27.7" + conditions: os=android & cpu=x64 + languageName: node + linkType: hard + "@esbuild/darwin-arm64@npm:0.25.12": version: 0.25.12 resolution: "@esbuild/darwin-arm64@npm:0.25.12" @@ -2266,6 +2329,13 @@ __metadata: languageName: node linkType: hard +"@esbuild/darwin-arm64@npm:0.27.7": + version: 0.27.7 + resolution: "@esbuild/darwin-arm64@npm:0.27.7" + conditions: os=darwin & cpu=arm64 + languageName: node + linkType: hard + "@esbuild/darwin-x64@npm:0.25.12": version: 0.25.12 resolution: "@esbuild/darwin-x64@npm:0.25.12" @@ -2273,6 +2343,13 @@ __metadata: languageName: node linkType: hard +"@esbuild/darwin-x64@npm:0.27.7": + version: 0.27.7 + resolution: "@esbuild/darwin-x64@npm:0.27.7" + conditions: os=darwin & cpu=x64 + languageName: node + linkType: hard + "@esbuild/freebsd-arm64@npm:0.25.12": version: 0.25.12 resolution: "@esbuild/freebsd-arm64@npm:0.25.12" @@ -2280,6 +2357,13 @@ __metadata: languageName: node linkType: hard +"@esbuild/freebsd-arm64@npm:0.27.7": + version: 0.27.7 + resolution: "@esbuild/freebsd-arm64@npm:0.27.7" + conditions: os=freebsd & cpu=arm64 + languageName: node + linkType: hard + "@esbuild/freebsd-x64@npm:0.25.12": version: 0.25.12 resolution: "@esbuild/freebsd-x64@npm:0.25.12" @@ -2287,6 +2371,13 @@ __metadata: languageName: node linkType: hard +"@esbuild/freebsd-x64@npm:0.27.7": + version: 0.27.7 + resolution: "@esbuild/freebsd-x64@npm:0.27.7" + conditions: os=freebsd & cpu=x64 + languageName: node + linkType: hard + "@esbuild/linux-arm64@npm:0.25.12": version: 0.25.12 resolution: "@esbuild/linux-arm64@npm:0.25.12" @@ -2294,6 +2385,13 @@ __metadata: languageName: node linkType: hard +"@esbuild/linux-arm64@npm:0.27.7": + version: 0.27.7 + resolution: "@esbuild/linux-arm64@npm:0.27.7" + conditions: os=linux & cpu=arm64 + languageName: node + linkType: hard + "@esbuild/linux-arm@npm:0.25.12": version: 0.25.12 resolution: "@esbuild/linux-arm@npm:0.25.12" @@ -2301,6 +2399,13 @@ __metadata: languageName: node linkType: hard +"@esbuild/linux-arm@npm:0.27.7": + version: 0.27.7 + resolution: "@esbuild/linux-arm@npm:0.27.7" + conditions: os=linux & cpu=arm + languageName: node + linkType: hard + "@esbuild/linux-ia32@npm:0.25.12": version: 0.25.12 resolution: "@esbuild/linux-ia32@npm:0.25.12" @@ -2308,6 +2413,13 @@ __metadata: languageName: node linkType: hard +"@esbuild/linux-ia32@npm:0.27.7": + version: 0.27.7 + resolution: "@esbuild/linux-ia32@npm:0.27.7" + conditions: os=linux & cpu=ia32 + languageName: node + linkType: hard + "@esbuild/linux-loong64@npm:0.25.12": version: 0.25.12 resolution: "@esbuild/linux-loong64@npm:0.25.12" @@ -2315,6 +2427,13 @@ __metadata: languageName: node linkType: hard +"@esbuild/linux-loong64@npm:0.27.7": + version: 0.27.7 + resolution: "@esbuild/linux-loong64@npm:0.27.7" + conditions: os=linux & cpu=loong64 + languageName: node + linkType: hard + "@esbuild/linux-mips64el@npm:0.25.12": version: 0.25.12 resolution: "@esbuild/linux-mips64el@npm:0.25.12" @@ -2322,6 +2441,13 @@ __metadata: languageName: node linkType: hard +"@esbuild/linux-mips64el@npm:0.27.7": + version: 0.27.7 + resolution: "@esbuild/linux-mips64el@npm:0.27.7" + conditions: os=linux & cpu=mips64el + languageName: node + linkType: hard + "@esbuild/linux-ppc64@npm:0.25.12": version: 0.25.12 resolution: "@esbuild/linux-ppc64@npm:0.25.12" @@ -2329,6 +2455,13 @@ __metadata: languageName: node linkType: hard +"@esbuild/linux-ppc64@npm:0.27.7": + version: 0.27.7 + resolution: "@esbuild/linux-ppc64@npm:0.27.7" + conditions: os=linux & cpu=ppc64 + languageName: node + linkType: hard + "@esbuild/linux-riscv64@npm:0.25.12": version: 0.25.12 resolution: "@esbuild/linux-riscv64@npm:0.25.12" @@ -2336,6 +2469,13 @@ __metadata: languageName: node linkType: hard +"@esbuild/linux-riscv64@npm:0.27.7": + version: 0.27.7 + resolution: "@esbuild/linux-riscv64@npm:0.27.7" + conditions: os=linux & cpu=riscv64 + languageName: node + linkType: hard + "@esbuild/linux-s390x@npm:0.25.12": version: 0.25.12 resolution: "@esbuild/linux-s390x@npm:0.25.12" @@ -2343,6 +2483,13 @@ __metadata: languageName: node linkType: hard +"@esbuild/linux-s390x@npm:0.27.7": + version: 0.27.7 + resolution: "@esbuild/linux-s390x@npm:0.27.7" + conditions: os=linux & cpu=s390x + languageName: node + linkType: hard + "@esbuild/linux-x64@npm:0.25.12": version: 0.25.12 resolution: "@esbuild/linux-x64@npm:0.25.12" @@ -2350,6 +2497,13 @@ __metadata: languageName: node linkType: hard +"@esbuild/linux-x64@npm:0.27.7": + version: 0.27.7 + resolution: "@esbuild/linux-x64@npm:0.27.7" + conditions: os=linux & cpu=x64 + languageName: node + linkType: hard + "@esbuild/netbsd-arm64@npm:0.25.12": version: 0.25.12 resolution: "@esbuild/netbsd-arm64@npm:0.25.12" @@ -2357,6 +2511,13 @@ __metadata: languageName: node linkType: hard +"@esbuild/netbsd-arm64@npm:0.27.7": + version: 0.27.7 + resolution: "@esbuild/netbsd-arm64@npm:0.27.7" + conditions: os=netbsd & cpu=arm64 + languageName: node + linkType: hard + "@esbuild/netbsd-x64@npm:0.25.12": version: 0.25.12 resolution: "@esbuild/netbsd-x64@npm:0.25.12" @@ -2364,6 +2525,13 @@ __metadata: languageName: node linkType: hard +"@esbuild/netbsd-x64@npm:0.27.7": + version: 0.27.7 + resolution: "@esbuild/netbsd-x64@npm:0.27.7" + conditions: os=netbsd & cpu=x64 + languageName: node + linkType: hard + "@esbuild/openbsd-arm64@npm:0.25.12": version: 0.25.12 resolution: "@esbuild/openbsd-arm64@npm:0.25.12" @@ -2371,6 +2539,13 @@ __metadata: languageName: node linkType: hard +"@esbuild/openbsd-arm64@npm:0.27.7": + version: 0.27.7 + resolution: "@esbuild/openbsd-arm64@npm:0.27.7" + conditions: os=openbsd & cpu=arm64 + languageName: node + linkType: hard + "@esbuild/openbsd-x64@npm:0.25.12": version: 0.25.12 resolution: "@esbuild/openbsd-x64@npm:0.25.12" @@ -2378,6 +2553,13 @@ __metadata: languageName: node linkType: hard +"@esbuild/openbsd-x64@npm:0.27.7": + version: 0.27.7 + resolution: "@esbuild/openbsd-x64@npm:0.27.7" + conditions: os=openbsd & cpu=x64 + languageName: node + linkType: hard + "@esbuild/openharmony-arm64@npm:0.25.12": version: 0.25.12 resolution: "@esbuild/openharmony-arm64@npm:0.25.12" @@ -2385,6 +2567,13 @@ __metadata: languageName: node linkType: hard +"@esbuild/openharmony-arm64@npm:0.27.7": + version: 0.27.7 + resolution: "@esbuild/openharmony-arm64@npm:0.27.7" + conditions: os=openharmony & cpu=arm64 + languageName: node + linkType: hard + "@esbuild/sunos-x64@npm:0.25.12": version: 0.25.12 resolution: "@esbuild/sunos-x64@npm:0.25.12" @@ -2392,6 +2581,13 @@ __metadata: languageName: node linkType: hard +"@esbuild/sunos-x64@npm:0.27.7": + version: 0.27.7 + resolution: "@esbuild/sunos-x64@npm:0.27.7" + conditions: os=sunos & cpu=x64 + languageName: node + linkType: hard + "@esbuild/win32-arm64@npm:0.25.12": version: 0.25.12 resolution: "@esbuild/win32-arm64@npm:0.25.12" @@ -2399,6 +2595,13 @@ __metadata: languageName: node linkType: hard +"@esbuild/win32-arm64@npm:0.27.7": + version: 0.27.7 + resolution: "@esbuild/win32-arm64@npm:0.27.7" + conditions: os=win32 & cpu=arm64 + languageName: node + linkType: hard + "@esbuild/win32-ia32@npm:0.25.12": version: 0.25.12 resolution: "@esbuild/win32-ia32@npm:0.25.12" @@ -2406,6 +2609,13 @@ __metadata: languageName: node linkType: hard +"@esbuild/win32-ia32@npm:0.27.7": + version: 0.27.7 + resolution: "@esbuild/win32-ia32@npm:0.27.7" + conditions: os=win32 & cpu=ia32 + languageName: node + linkType: hard + "@esbuild/win32-x64@npm:0.25.12": version: 0.25.12 resolution: "@esbuild/win32-x64@npm:0.25.12" @@ -2413,6 +2623,13 @@ __metadata: languageName: node linkType: hard +"@esbuild/win32-x64@npm:0.27.7": + version: 0.27.7 + resolution: "@esbuild/win32-x64@npm:0.27.7" + conditions: os=win32 & cpu=x64 + languageName: node + linkType: hard + "@eslint-community/eslint-utils@npm:^4.2.0, @eslint-community/eslint-utils@npm:^4.4.0": version: 4.4.1 resolution: "@eslint-community/eslint-utils@npm:4.4.1" @@ -3004,7 +3221,7 @@ __metadata: languageName: node linkType: hard -"@jridgewell/sourcemap-codec@npm:^1.5.0": +"@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: c2e36e67971f719a8a3a85ef5a5f580622437cc723c35d03ebd0c9c0b06418700ef006f58af742791f71f6a4fc68fcfaf1f6a74ec2f9a3332860e9373459dae7 @@ -3021,7 +3238,7 @@ __metadata: languageName: node linkType: hard -"@jridgewell/trace-mapping@npm:^0.3.28": +"@jridgewell/trace-mapping@npm:^0.3.23, @jridgewell/trace-mapping@npm:^0.3.28, @jridgewell/trace-mapping@npm:^0.3.31": version: 0.3.31 resolution: "@jridgewell/trace-mapping@npm:0.3.31" dependencies: @@ -4109,6 +4326,16 @@ __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": "*" + assertion-error: ^2.0.1 + checksum: eb4c2da9ec38b474a983f39bfb5ec4fbcceb5e5d76d184094d2cbc4c41357973eb5769c8972cedac665a233251b0ed754f1e338fcf408d381968af85cdecc596 + languageName: node + linkType: hard + "@types/connect-history-api-fallback@npm:^1.3.5": version: 1.5.4 resolution: "@types/connect-history-api-fallback@npm:1.5.4" @@ -4266,6 +4493,13 @@ __metadata: languageName: node linkType: hard +"@types/deep-eql@npm:*": + version: 4.0.2 + resolution: "@types/deep-eql@npm:4.0.2" + checksum: 249a27b0bb22f6aa28461db56afa21ec044fa0e303221a62dff81831b20c8530502175f1a49060f7099e7be06181078548ac47c668de79ff9880241968d43d0c + languageName: node + linkType: hard + "@types/eslint-scope@npm:^3.7.7": version: 3.7.7 resolution: "@types/eslint-scope@npm:3.7.7" @@ -4310,7 +4544,7 @@ __metadata: languageName: node linkType: hard -"@types/estree@npm:1.0.8, @types/estree@npm:^1.0.8": +"@types/estree@npm:1.0.8, @types/estree@npm:^1.0.0, @types/estree@npm:^1.0.8": version: 1.0.8 resolution: "@types/estree@npm:1.0.8" checksum: bd93e2e415b6f182ec4da1074e1f36c480f1d26add3e696d54fb30c09bc470897e41361c8fd957bf0985024f8fbf1e6e2aff977d79352ef7eb93a5c6dcff6c11 @@ -5274,6 +5508,116 @@ __metadata: languageName: node linkType: hard +"@vitest/coverage-v8@npm:^3.2.4": + version: 3.2.4 + resolution: "@vitest/coverage-v8@npm:3.2.4" + dependencies: + "@ampproject/remapping": ^2.3.0 + "@bcoe/v8-coverage": ^1.0.2 + ast-v8-to-istanbul: ^0.3.3 + debug: ^4.4.1 + istanbul-lib-coverage: ^3.2.2 + istanbul-lib-report: ^3.0.1 + istanbul-lib-source-maps: ^5.0.6 + istanbul-reports: ^3.1.7 + magic-string: ^0.30.17 + magicast: ^0.3.5 + std-env: ^3.9.0 + test-exclude: ^7.0.1 + tinyrainbow: ^2.0.0 + peerDependencies: + "@vitest/browser": 3.2.4 + vitest: 3.2.4 + peerDependenciesMeta: + "@vitest/browser": + optional: true + checksum: b33d4abb32216c793b1da122254bf70578c4acddbf2011c377818cbfc9506383398a5a2eaeb70045120dac1f86a1a2511fd624050dd92ef7387b9469929ceb33 + languageName: node + linkType: hard + +"@vitest/expect@npm:3.2.4": + version: 3.2.4 + resolution: "@vitest/expect@npm:3.2.4" + dependencies: + "@types/chai": ^5.2.2 + "@vitest/spy": 3.2.4 + "@vitest/utils": 3.2.4 + chai: ^5.2.0 + tinyrainbow: ^2.0.0 + checksum: 57627ee2b47555f47a15843fda05267816e9767e5a769179acac224b8682844e662fa77fbeeb04adcb0874779f3aca861f54e9fc630c1d256d5ea8211c223120 + languageName: node + linkType: hard + +"@vitest/mocker@npm:3.2.4": + version: 3.2.4 + resolution: "@vitest/mocker@npm:3.2.4" + dependencies: + "@vitest/spy": 3.2.4 + estree-walker: ^3.0.3 + magic-string: ^0.30.17 + peerDependencies: + msw: ^2.4.9 + vite: ^5.0.0 || ^6.0.0 || ^7.0.0-0 + peerDependenciesMeta: + msw: + optional: true + vite: + optional: true + checksum: 2c8ba286fc714036b645a7a72bfbbd6b243baa65320dd71009f5ed1115f70f69c0209e2e213a05202c172e09a408821a33f9df5bc7979900e91cde5d302976e0 + languageName: node + linkType: hard + +"@vitest/pretty-format@npm:3.2.4, @vitest/pretty-format@npm:^3.2.4": + version: 3.2.4 + resolution: "@vitest/pretty-format@npm:3.2.4" + dependencies: + tinyrainbow: ^2.0.0 + checksum: 68a196e4bdfce6fd03c3958b76cddb71bec65a62ab5aff05ba743a44853b03a95c2809b4e5733d21abff25c4d070dd64f60c81ac973a9fd21a840ff8f8a8d184 + languageName: node + linkType: hard + +"@vitest/runner@npm:3.2.4": + version: 3.2.4 + resolution: "@vitest/runner@npm:3.2.4" + dependencies: + "@vitest/utils": 3.2.4 + pathe: ^2.0.3 + strip-literal: ^3.0.0 + checksum: c8b08365818f408eec2fe3acbffa0cc7279939a43c02074cd03b853fa37bc68aa181c8f8c2175513a4c5aa4dd3e52a0573d5897a16846d55b2ff4f3577e6c7c8 + languageName: node + linkType: hard + +"@vitest/snapshot@npm:3.2.4": + version: 3.2.4 + resolution: "@vitest/snapshot@npm:3.2.4" + dependencies: + "@vitest/pretty-format": 3.2.4 + magic-string: ^0.30.17 + pathe: ^2.0.3 + checksum: 2f00fb83d5c9ed1f2a79323db3993403bd34265314846cb1bcf1cb9b68f56dfde5ee5a4a8dcb6d95317835bc203662e333da6841e50800c6707e0d22e48ebe6e + languageName: node + linkType: hard + +"@vitest/spy@npm:3.2.4": + version: 3.2.4 + resolution: "@vitest/spy@npm:3.2.4" + dependencies: + tinyspy: ^4.0.3 + checksum: 0e3b591e0c67275b747c5aa67946d6496cd6759dd9b8e05c524426207ca9631fe2cae8ac85a8ba22acec4a593393cd97d825f88a42597fc65441f0b633986f49 + languageName: node + linkType: hard + +"@vitest/utils@npm:3.2.4": + version: 3.2.4 + resolution: "@vitest/utils@npm:3.2.4" + dependencies: + "@vitest/pretty-format": 3.2.4 + loupe: ^3.1.4 + tinyrainbow: ^2.0.0 + checksum: 6b0fd0075c23b8e3f17ecf315adc1e565e5a9e7d1b8ad78bbccf2505e399855d176254d974587c00bc4396a0e348bae1380e780a1e7f6b97ea6399a9ab665ba7 + languageName: node + linkType: hard + "@webassemblyjs/ast@npm:1.14.1, @webassemblyjs/ast@npm:^1.14.1": version: 1.14.1 resolution: "@webassemblyjs/ast@npm:1.14.1" @@ -5963,6 +6307,13 @@ __metadata: languageName: node linkType: hard +"assertion-error@npm:^2.0.1": + version: 2.0.1 + resolution: "assertion-error@npm:2.0.1" + checksum: a0789dd882211b87116e81e2648ccb7f60340b34f19877dd020b39ebb4714e475eb943e14ba3e22201c221ef6645b7bfe10297e76b6ac95b48a9898c1211ce66 + languageName: node + linkType: hard + "ast-types-flow@npm:^0.0.8": version: 0.0.8 resolution: "ast-types-flow@npm:0.0.8" @@ -5970,6 +6321,17 @@ __metadata: languageName: node linkType: hard +"ast-v8-to-istanbul@npm:^0.3.3": + version: 0.3.12 + resolution: "ast-v8-to-istanbul@npm:0.3.12" + dependencies: + "@jridgewell/trace-mapping": ^0.3.31 + estree-walker: ^3.0.3 + js-tokens: ^10.0.0 + checksum: a055db37872d927f5ec95d7825a1d6820345b32f6fc29182c8617c0f859b22027f6d0815c6da7685c02faa3a540f79e5e74f1ebf4ce4e7b066e3ea4895f8497c + languageName: node + linkType: hard + "astral-regex@npm:^2.0.0": version: 2.0.0 resolution: "astral-regex@npm:2.0.0" @@ -6260,6 +6622,13 @@ __metadata: languageName: node linkType: hard +"balanced-match@npm:^4.0.2": + version: 4.0.4 + resolution: "balanced-match@npm:4.0.4" + checksum: fb07bb66a0959c2843fc055838047e2a95ccebb837c519614afb067ebfdf2fa967ca8d712c35ced07f2cd26fc6f07964230b094891315ad74f11eba3d53178a0 + languageName: node + linkType: hard + "base64-js@npm:^1.3.1, base64-js@npm:^1.5.1": version: 1.5.1 resolution: "base64-js@npm:1.5.1" @@ -6405,6 +6774,15 @@ __metadata: languageName: node linkType: hard +"brace-expansion@npm:^5.0.5": + version: 5.0.5 + resolution: "brace-expansion@npm:5.0.5" + dependencies: + balanced-match: ^4.0.2 + checksum: 4481b7ffa467b34c14e258167dbd8d9485a2d31d03060e8e8b38142dcde32cdc89c8f55b04d3ae7aae9304fa7eac1dfafd602787cf09c019cc45de3bb6950ffc + languageName: node + linkType: hard + "braces@npm:^3.0.3, braces@npm:~3.0.2": version: 3.0.3 resolution: "braces@npm:3.0.3" @@ -6521,6 +6899,13 @@ __metadata: languageName: node linkType: hard +"cac@npm:^6.7.14": + version: 6.7.14 + resolution: "cac@npm:6.7.14" + checksum: 45a2496a9443abbe7f52a49b22fbe51b1905eff46e03fd5e6c98e3f85077be3f8949685a1849b1a9cd2bc3e5567dfebcf64f01ce01847baf918f1b37c839791a + languageName: node + linkType: hard + "cacache@npm:^18.0.0": version: 18.0.4 resolution: "cacache@npm:18.0.4" @@ -6656,6 +7041,19 @@ __metadata: languageName: node linkType: hard +"chai@npm:^5.2.0": + version: 5.3.3 + resolution: "chai@npm:5.3.3" + dependencies: + assertion-error: ^2.0.1 + check-error: ^2.1.1 + deep-eql: ^5.0.1 + loupe: ^3.1.0 + pathval: ^2.0.0 + checksum: bc4091f1cccfee63f6a3d02ce477fe847f5c57e747916a11bd72675c9459125084e2e55dc2363ee2b82b088a878039ee7ee27c75d6d90f7de9202bf1b12ce573 + languageName: node + linkType: hard + "chalk-template@npm:0.4.0": version: 0.4.0 resolution: "chalk-template@npm:0.4.0" @@ -6714,6 +7112,13 @@ __metadata: languageName: node linkType: hard +"check-error@npm:^2.1.1": + version: 2.1.3 + resolution: "check-error@npm:2.1.3" + checksum: f1868d3db60f5a7da92e140ccf33e9152bf6124161fa9b7a4ae8eafdb05e66e1f13570401e56f314f037b0f1b71eaf38ad0c7256310d82c6105e9d85ded0f202 + languageName: node + linkType: hard + "check-more-types@npm:^2.24.0": version: 2.24.0 resolution: "check-more-types@npm:2.24.0" @@ -7844,6 +8249,18 @@ __metadata: languageName: node linkType: hard +"debug@npm:^4.4.1": + version: 4.4.3 + resolution: "debug@npm:4.4.3" + dependencies: + ms: ^2.1.3 + peerDependenciesMeta: + supports-color: + optional: true + checksum: 4805abd570e601acdca85b6aa3757186084a45cff9b2fa6eee1f3b173caa776b45f478b2a71a572d616d2010cea9211d0ac4a02a610e4c18ac4324bde3760834 + languageName: node + linkType: hard + "decimal.js@npm:^10.2.1": version: 10.4.3 resolution: "decimal.js@npm:10.4.3" @@ -7858,6 +8275,13 @@ __metadata: languageName: node linkType: hard +"deep-eql@npm:^5.0.1": + version: 5.0.2 + resolution: "deep-eql@npm:5.0.2" + checksum: 6aaaadb4c19cbce42e26b2bbe5bd92875f599d2602635dc97f0294bae48da79e89470aedee05f449e0ca8c65e9fd7e7872624d1933a1db02713d99c2ca8d1f24 + languageName: node + linkType: hard + "deep-extend@npm:^0.6.0": version: 0.6.0 resolution: "deep-extend@npm:0.6.0" @@ -8476,6 +8900,13 @@ __metadata: languageName: node linkType: hard +"es-module-lexer@npm:^1.7.0": + version: 1.7.0 + resolution: "es-module-lexer@npm:1.7.0" + checksum: 7858bb76ae387fdbf8a6fccc951bf18919768309850587553eca34698b9193fbc65fab03d3d9f69163d860321fbf66adf89d5821e7f4148c7cb7d7b997259211 + languageName: node + linkType: hard + "es-module-lexer@npm:^2.0.0": version: 2.0.0 resolution: "es-module-lexer@npm:2.0.0" @@ -8633,6 +9064,95 @@ __metadata: languageName: node linkType: hard +"esbuild@npm:^0.27.0": + version: 0.27.7 + resolution: "esbuild@npm:0.27.7" + dependencies: + "@esbuild/aix-ppc64": 0.27.7 + "@esbuild/android-arm": 0.27.7 + "@esbuild/android-arm64": 0.27.7 + "@esbuild/android-x64": 0.27.7 + "@esbuild/darwin-arm64": 0.27.7 + "@esbuild/darwin-x64": 0.27.7 + "@esbuild/freebsd-arm64": 0.27.7 + "@esbuild/freebsd-x64": 0.27.7 + "@esbuild/linux-arm": 0.27.7 + "@esbuild/linux-arm64": 0.27.7 + "@esbuild/linux-ia32": 0.27.7 + "@esbuild/linux-loong64": 0.27.7 + "@esbuild/linux-mips64el": 0.27.7 + "@esbuild/linux-ppc64": 0.27.7 + "@esbuild/linux-riscv64": 0.27.7 + "@esbuild/linux-s390x": 0.27.7 + "@esbuild/linux-x64": 0.27.7 + "@esbuild/netbsd-arm64": 0.27.7 + "@esbuild/netbsd-x64": 0.27.7 + "@esbuild/openbsd-arm64": 0.27.7 + "@esbuild/openbsd-x64": 0.27.7 + "@esbuild/openharmony-arm64": 0.27.7 + "@esbuild/sunos-x64": 0.27.7 + "@esbuild/win32-arm64": 0.27.7 + "@esbuild/win32-ia32": 0.27.7 + "@esbuild/win32-x64": 0.27.7 + dependenciesMeta: + "@esbuild/aix-ppc64": + optional: true + "@esbuild/android-arm": + optional: true + "@esbuild/android-arm64": + optional: true + "@esbuild/android-x64": + optional: true + "@esbuild/darwin-arm64": + optional: true + "@esbuild/darwin-x64": + optional: true + "@esbuild/freebsd-arm64": + optional: true + "@esbuild/freebsd-x64": + optional: true + "@esbuild/linux-arm": + optional: true + "@esbuild/linux-arm64": + optional: true + "@esbuild/linux-ia32": + optional: true + "@esbuild/linux-loong64": + optional: true + "@esbuild/linux-mips64el": + optional: true + "@esbuild/linux-ppc64": + optional: true + "@esbuild/linux-riscv64": + optional: true + "@esbuild/linux-s390x": + optional: true + "@esbuild/linux-x64": + optional: true + "@esbuild/netbsd-arm64": + optional: true + "@esbuild/netbsd-x64": + optional: true + "@esbuild/openbsd-arm64": + optional: true + "@esbuild/openbsd-x64": + optional: true + "@esbuild/openharmony-arm64": + optional: true + "@esbuild/sunos-x64": + optional: true + "@esbuild/win32-arm64": + optional: true + "@esbuild/win32-ia32": + optional: true + "@esbuild/win32-x64": + optional: true + bin: + esbuild: bin/esbuild + checksum: ea7ee3c039b83caae1b59bb7ae2db9c6bceb21bccb033dbc4c0c45cb159554ead4289492ad874857bbca7f6e37bf74e04e892544de2b3c5c7888c8ef8beaf2f7 + languageName: node + linkType: hard + "escalade@npm:^3.1.1, escalade@npm:^3.2.0": version: 3.2.0 resolution: "escalade@npm:3.2.0" @@ -9107,6 +9627,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": ^1.0.0 + checksum: a65728d5727b71de172c5df323385755a16c0fdab8234dc756c3854cfee343261ddfbb72a809a5660fac8c75d960bb3e21aa898c2d7e9b19bb298482ca58a3af + languageName: node + linkType: hard + "esutils@npm:^2.0.2": version: 2.0.3 resolution: "esutils@npm:2.0.3" @@ -9216,6 +9745,13 @@ __metadata: languageName: node linkType: hard +"expect-type@npm:^1.2.1": + version: 1.3.0 + resolution: "expect-type@npm:1.3.0" + checksum: 60476b4f4c0c88bf24db0735faa7d1d0c9120c21e5b78781c0fea0d4a95838f2db0c919a055aa4bb185ccbf38e37fa3000d3bb05500ceafcc7c469955c5a4f84 + languageName: node + linkType: hard + "expect@npm:^27.5.1": version: 27.5.1 resolution: "expect@npm:27.5.1" @@ -9457,6 +9993,7 @@ __metadata: "@typescript-eslint/eslint-plugin": 8.16.0 "@typescript-eslint/parser": 8.16.0 "@vitejs/plugin-react": ^4.3.4 + "@vitest/coverage-v8": ^3.2.4 axios: ^1.15.0 cross-env: ^7.0.3 cypress: ^13.16.0 @@ -9493,6 +10030,7 @@ __metadata: typescript: 5.7.x utif: ^3.1.0 vite: ^6.4.2 + vitest: ^3.2.4 ws: 8.18.0 languageName: unknown linkType: soft @@ -10062,6 +10600,22 @@ __metadata: languageName: node linkType: hard +"glob@npm:^10.4.1": + version: 10.5.0 + resolution: "glob@npm:10.5.0" + dependencies: + foreground-child: ^3.1.0 + jackspeak: ^3.1.2 + minimatch: ^9.0.4 + minipass: ^7.1.2 + package-json-from-dist: ^1.0.0 + path-scurry: ^1.11.1 + bin: + glob: dist/esm/bin.mjs + checksum: cda96c074878abca9657bd984d2396945cf0d64283f6feeb40d738fe2da642be0010ad5210a1646244a5fc3511b0cab5a374569b3de5a12b8a63d392f18c6043 + languageName: node + linkType: hard + "glob@npm:^7.1.1, glob@npm:^7.1.2, glob@npm:^7.1.3, glob@npm:^7.1.4, glob@npm:^7.1.6": version: 7.2.3 resolution: "glob@npm:7.2.3" @@ -11260,7 +11814,7 @@ __metadata: languageName: node linkType: hard -"istanbul-lib-coverage@npm:^3.0.0, istanbul-lib-coverage@npm:^3.2.0": +"istanbul-lib-coverage@npm:^3.0.0, istanbul-lib-coverage@npm:^3.2.0, istanbul-lib-coverage@npm:^3.2.2": version: 3.2.2 resolution: "istanbul-lib-coverage@npm:3.2.2" checksum: 2367407a8d13982d8f7a859a35e7f8dd5d8f75aae4bb5484ede3a9ea1b426dc245aff28b976a2af48ee759fdd9be374ce2bd2669b644f31e76c5f46a2e29a831 @@ -11280,7 +11834,7 @@ __metadata: languageName: node linkType: hard -"istanbul-lib-report@npm:^3.0.0": +"istanbul-lib-report@npm:^3.0.0, istanbul-lib-report@npm:^3.0.1": version: 3.0.1 resolution: "istanbul-lib-report@npm:3.0.1" dependencies: @@ -11302,6 +11856,17 @@ __metadata: languageName: node linkType: hard +"istanbul-lib-source-maps@npm:^5.0.6": + version: 5.0.6 + resolution: "istanbul-lib-source-maps@npm:5.0.6" + dependencies: + "@jridgewell/trace-mapping": ^0.3.23 + debug: ^4.1.1 + istanbul-lib-coverage: ^3.0.0 + checksum: 8dd6f2c1e2ecaacabeef8dc9ab52c4ed0a6036310002cf7f46ea6f3a5fb041da8076f5350e6a6be4c60cd4f231c51c73e042044afaf44820d857d92ecfb8ab6c + languageName: node + linkType: hard + "istanbul-reports@npm:^3.1.3": version: 3.1.7 resolution: "istanbul-reports@npm:3.1.7" @@ -11312,6 +11877,16 @@ __metadata: languageName: node linkType: hard +"istanbul-reports@npm:^3.1.7": + version: 3.2.0 + resolution: "istanbul-reports@npm:3.2.0" + dependencies: + html-escaper: ^2.0.0 + istanbul-lib-report: ^3.0.0 + checksum: 72b4c8525276147908d28b0917bc675b1019836b638e50875521ca3b8ec63672681aa98dbab88a6f49ef798c08fe041d428abdcf84f4f3fcff5844eee54af65a + languageName: node + linkType: hard + "iterator.prototype@npm:^1.1.3": version: 1.1.3 resolution: "iterator.prototype@npm:1.1.3" @@ -12016,6 +12591,13 @@ __metadata: languageName: node linkType: hard +"js-tokens@npm:^10.0.0": + version: 10.0.0 + resolution: "js-tokens@npm:10.0.0" + checksum: 1bc804299157b0a81543f9975d380aaf9e4bd57853e41623a10ebd364dad30682d2f194d1b0485832cd85a59a0bef21fbfc7a063a95575b782ddecaa804647fb + languageName: node + linkType: hard + "js-tokens@npm:^3.0.0 || ^4.0.0, js-tokens@npm:^4.0.0": version: 4.0.0 resolution: "js-tokens@npm:4.0.0" @@ -12023,6 +12605,13 @@ __metadata: languageName: node linkType: hard +"js-tokens@npm:^9.0.1": + version: 9.0.1 + resolution: "js-tokens@npm:9.0.1" + checksum: 8b604020b1a550e575404bfdde4d12c11a7991ffe0c58a2cf3515b9a512992dc7010af788f0d8b7485e403d462d9e3d3b96c4ff03201550fdbb09e17c811e054 + languageName: node + linkType: hard + "js-yaml@npm:^3.13.1": version: 3.14.2 resolution: "js-yaml@npm:3.14.2" @@ -12671,6 +13260,13 @@ __metadata: languageName: node linkType: hard +"loupe@npm:^3.1.0, loupe@npm:^3.1.4": + version: 3.2.1 + resolution: "loupe@npm:3.2.1" + checksum: 3ce9ecc5b2c56ffc073bf065ad3a4644cccce3eac81e61a8732e9c8ebfe05513ed478592d25f9dba24cfe82766913be045ab384c04711c7c6447deaf800ad94c + languageName: node + linkType: hard + "lower-case@npm:^2.0.2": version: 2.0.2 resolution: "lower-case@npm:2.0.2" @@ -12705,6 +13301,26 @@ __metadata: languageName: node linkType: hard +"magic-string@npm:^0.30.17": + version: 0.30.21 + resolution: "magic-string@npm:0.30.21" + dependencies: + "@jridgewell/sourcemap-codec": ^1.5.5 + checksum: 4ff76a4e8d439431cf49f039658751ed351962d044e5955adc257489569bd676019c906b631f86319217689d04815d7d064ee3ff08ab82ae65b7655a7e82a414 + languageName: node + linkType: hard + +"magicast@npm:^0.3.5": + version: 0.3.5 + resolution: "magicast@npm:0.3.5" + dependencies: + "@babel/parser": ^7.25.4 + "@babel/types": ^7.25.4 + source-map-js: ^1.2.0 + checksum: 668f07ade907a44bccfc9a9321588473f6d5fa25329aa26b9ad9a3bf87cc2e6f9c482cbdd3e33c0b9ab9b79c065630c599cc055a12f881c8c924ee0d7282cdce + languageName: node + linkType: hard + "make-dir@npm:^3.0.2, make-dir@npm:^3.1.0": version: 3.1.0 resolution: "make-dir@npm:3.1.0" @@ -12944,6 +13560,15 @@ __metadata: languageName: node linkType: hard +"minimatch@npm:^10.2.2": + version: 10.2.5 + resolution: "minimatch@npm:10.2.5" + dependencies: + brace-expansion: ^5.0.5 + checksum: 000423875fecbc7da1d74bf63c9081363a71291ef2588c376c45647ac004582cb5bc8cc09ef84420b26bfb490f4d0818d328e78569c6228e20d90271283f73ba + languageName: node + linkType: hard + "minimatch@npm:^5.0.1": version: 5.1.6 resolution: "minimatch@npm:5.1.6" @@ -13760,6 +14385,20 @@ __metadata: languageName: node linkType: hard +"pathe@npm:^2.0.3": + version: 2.0.3 + resolution: "pathe@npm:2.0.3" + checksum: 0602bdd4acb54d91044e0c56f1fb63467ae7d44ab3afea1f797947b0eb2b4d1d91cf0d58d065fdb0a8ab0c4acbbd8d3a5b424983eaf10dd5285d37a16f6e3ee9 + languageName: node + linkType: hard + +"pathval@npm:^2.0.0": + version: 2.0.1 + resolution: "pathval@npm:2.0.1" + checksum: 280e71cfd86bb5d7ff371fe2752997e5fa82901fcb209abf19d4457b7814f1b4a17845dfb17bd28a596ccdb0ecea178720ce23dacfa9c841f37804b700647810 + languageName: node + linkType: hard + "pend@npm:~1.2.0": version: 1.2.0 resolution: "pend@npm:1.2.0" @@ -13795,7 +14434,7 @@ __metadata: languageName: node linkType: hard -"picomatch@npm:^4.0.2, picomatch@npm:^4.0.4": +"picomatch@npm:^4.0.2, picomatch@npm:^4.0.3, picomatch@npm:^4.0.4": version: 4.0.4 resolution: "picomatch@npm:4.0.4" checksum: 76b387b5157951422fa6049a96bdd1695e39dd126cd99df34d343638dc5cdb8bcdc83fff288c23eddcf7c26657c35e3173d4d5f488c4f28b889b314472e0a662 @@ -14719,6 +15358,17 @@ __metadata: languageName: node linkType: hard +"postcss@npm:^8.5.6": + version: 8.5.10 + resolution: "postcss@npm:8.5.10" + dependencies: + nanoid: ^3.3.11 + picocolors: ^1.1.1 + source-map-js: ^1.2.1 + checksum: 9af9cd7f2f0d4b8456f6710e48d586328433509b695911fda942c24ac4db4e62c6fed8c6c6d8c8258326285f669494c2c36a4ff84aa160f0586eb545e5258bf5 + languageName: node + linkType: hard + "prelude-ls@npm:^1.2.1": version: 1.2.1 resolution: "prelude-ls@npm:1.2.1" @@ -15858,7 +16508,7 @@ __metadata: languageName: node linkType: hard -"rollup@npm:^4.34.9": +"rollup@npm:^4.34.9, rollup@npm:^4.43.0": version: 4.60.1 resolution: "rollup@npm:4.60.1" dependencies: @@ -16354,6 +17004,13 @@ __metadata: languageName: node linkType: hard +"siginfo@npm:^2.0.0": + version: 2.0.0 + resolution: "siginfo@npm:2.0.0" + checksum: 8aa5a98640ca09fe00d74416eca97551b3e42991614a3d1b824b115fc1401543650914f651ab1311518177e4d297e80b953f4cd4cd7ea1eabe824e8f2091de01 + languageName: node + linkType: hard + "signal-exit@npm:^3.0.2, signal-exit@npm:^3.0.3": version: 3.0.7 resolution: "signal-exit@npm:3.0.7" @@ -16490,7 +17147,7 @@ __metadata: languageName: node linkType: hard -"source-map-js@npm:^1.0.1, source-map-js@npm:^1.2.1": +"source-map-js@npm:^1.0.1, source-map-js@npm:^1.2.0, source-map-js@npm:^1.2.1": version: 1.2.1 resolution: "source-map-js@npm:1.2.1" checksum: 4eb0cd997cdf228bc253bcaff9340afeb706176e64868ecd20efbe6efea931465f43955612346d6b7318789e5265bdc419bc7669c1cebe3db0eb255f57efa76b @@ -16655,6 +17312,13 @@ __metadata: languageName: node linkType: hard +"stackback@npm:0.0.2": + version: 0.0.2 + resolution: "stackback@npm:0.0.2" + checksum: 2d4dc4e64e2db796de4a3c856d5943daccdfa3dd092e452a1ce059c81e9a9c29e0b9badba91b43ef0d5ff5c04ee62feb3bcc559a804e16faf447bac2d883aa99 + languageName: node + linkType: hard + "stackframe@npm:^1.3.4": version: 1.3.4 resolution: "stackframe@npm:1.3.4" @@ -16692,6 +17356,13 @@ __metadata: languageName: node linkType: hard +"std-env@npm:^3.9.0": + version: 3.10.0 + resolution: "std-env@npm:3.10.0" + checksum: 51d641b36b0fae494a546fb8446d39a837957fbf902c765c62bd12af8e50682d141c4087ca032f1192fa90330c4f6ff23fd6c9795324efacd1684e814471e0e0 + languageName: node + linkType: hard + "string-argv@npm:~0.3.2": version: 0.3.2 resolution: "string-argv@npm:0.3.2" @@ -16939,6 +17610,15 @@ __metadata: languageName: node linkType: hard +"strip-literal@npm:^3.0.0": + version: 3.1.0 + resolution: "strip-literal@npm:3.1.0" + dependencies: + js-tokens: ^9.0.1 + checksum: c9758eea9085cea6178f06a59af6be62382efe7a5ddb6f12f86e37818adae734774d61b1171f153cf0bbd61718155e8182d9fa8a87620047d1ed90eadc965e9a + languageName: node + linkType: hard + "style-loader@npm:^3.3.1": version: 3.3.4 resolution: "style-loader@npm:3.3.4" @@ -17302,6 +17982,17 @@ __metadata: languageName: node linkType: hard +"test-exclude@npm:^7.0.1": + version: 7.0.2 + resolution: "test-exclude@npm:7.0.2" + dependencies: + "@istanbuljs/schema": ^0.1.2 + glob: ^10.4.1 + minimatch: ^10.2.2 + checksum: 35fd6304435c31e6b3dd4f5ad16de521eef46d433c4d2f2ef81a981b002a99db70b12d372f042df48a38f9bd356eb2a3d5523d75ec2121671e400f5468e07bd1 + languageName: node + linkType: hard + "text-table@npm:^0.2.0": version: 0.2.0 resolution: "text-table@npm:0.2.0" @@ -17383,7 +18074,21 @@ __metadata: languageName: node linkType: hard -"tinyglobby@npm:^0.2.13": +"tinybench@npm:^2.9.0": + version: 2.9.0 + resolution: "tinybench@npm:2.9.0" + checksum: 1ab00d7dfe0d1f127cbf00822bacd9024f7a50a3ecd1f354a8168e0b7d2b53a639a24414e707c27879d1adc0f5153141d51d76ebd7b4d37fe245e742e5d91fe8 + languageName: node + linkType: hard + +"tinyexec@npm:^0.3.2": + version: 0.3.2 + resolution: "tinyexec@npm:0.3.2" + checksum: bd491923020610bdeadb0d8cf5d70e7cbad5a3201620fd01048c9bf3b31ffaa75c33254e1540e13b993ce4e8187852b0b5a93057bb598e7a57afa2ca2048a35c + languageName: node + linkType: hard + +"tinyglobby@npm:^0.2.13, tinyglobby@npm:^0.2.14, tinyglobby@npm:^0.2.15": version: 0.2.16 resolution: "tinyglobby@npm:0.2.16" dependencies: @@ -17393,6 +18098,27 @@ __metadata: languageName: node linkType: hard +"tinypool@npm:^1.1.1": + version: 1.1.1 + resolution: "tinypool@npm:1.1.1" + checksum: 0258abe108df8be395a2cbdc8b4390c94908850250530f7bea83a129fa33d49a8c93246f76bf81cd458534abd81322f4d4cb3a40690254f8d9044ff449f328a8 + languageName: node + linkType: hard + +"tinyrainbow@npm:^2.0.0": + version: 2.0.0 + resolution: "tinyrainbow@npm:2.0.0" + checksum: 26360631d97e43955a07cfb70fe40a154ce4e2bcd14fa3d37ce8e2ed8f4fa9e5ba00783e4906bbfefe6dcabef5d3510f5bee207cb693bee4e4e7553f5454bef1 + languageName: node + linkType: hard + +"tinyspy@npm:^4.0.3": + version: 4.0.4 + resolution: "tinyspy@npm:4.0.4" + checksum: 858a99e3ded2fba8fe7c243099d9e58e926d6525af03d19cdf86c1a9a30398161fb830b4f77890d266bcc1c69df08fa6f4baf29d089385e4cdaa98d7b6296e7c + languageName: node + linkType: hard + "tldts-core@npm:^6.1.64": version: 6.1.64 resolution: "tldts-core@npm:6.1.64" @@ -17988,6 +18714,76 @@ __metadata: languageName: node linkType: hard +"vite-node@npm:3.2.4": + version: 3.2.4 + resolution: "vite-node@npm:3.2.4" + dependencies: + cac: ^6.7.14 + debug: ^4.4.1 + es-module-lexer: ^1.7.0 + pathe: ^2.0.3 + vite: ^5.0.0 || ^6.0.0 || ^7.0.0-0 + bin: + vite-node: vite-node.mjs + checksum: 2051394d48f5eefdee4afc9c5fd5dcbf7eb36d345043ba035c7782e10b33fbbd14318062c4e32e00d473a31a559fb628d67c023e82a4903016db3ac6bfdb3fe7 + languageName: node + linkType: hard + +"vite@npm:^5.0.0 || ^6.0.0 || ^7.0.0-0": + version: 7.3.2 + resolution: "vite@npm:7.3.2" + dependencies: + esbuild: ^0.27.0 + fdir: ^6.5.0 + fsevents: ~2.3.3 + picomatch: ^4.0.3 + postcss: ^8.5.6 + rollup: ^4.43.0 + tinyglobby: ^0.2.15 + peerDependencies: + "@types/node": ^20.19.0 || >=22.12.0 + jiti: ">=1.21.0" + less: ^4.0.0 + lightningcss: ^1.21.0 + sass: ^1.70.0 + sass-embedded: ^1.70.0 + stylus: ">=0.54.8" + sugarss: ^5.0.0 + terser: ^5.16.0 + tsx: ^4.8.1 + yaml: ^2.4.2 + dependenciesMeta: + fsevents: + optional: true + peerDependenciesMeta: + "@types/node": + optional: true + jiti: + optional: true + less: + optional: true + lightningcss: + optional: true + sass: + optional: true + sass-embedded: + optional: true + stylus: + optional: true + sugarss: + optional: true + terser: + optional: true + tsx: + optional: true + yaml: + optional: true + bin: + vite: bin/vite.js + checksum: f98bf7feb114794ae4b1ccbb2363f0417fd7a8c7e493e8cbc988b39313401db15d2ff64737eb8f10d805458c028a67b81395afcbff5b5fa70a70ffa2ec70aa52 + languageName: node + linkType: hard + "vite@npm:^6.4.2": version: 6.4.2 resolution: "vite@npm:6.4.2" @@ -18043,6 +18839,62 @@ __metadata: languageName: node linkType: hard +"vitest@npm:^3.2.4": + version: 3.2.4 + resolution: "vitest@npm:3.2.4" + dependencies: + "@types/chai": ^5.2.2 + "@vitest/expect": 3.2.4 + "@vitest/mocker": 3.2.4 + "@vitest/pretty-format": ^3.2.4 + "@vitest/runner": 3.2.4 + "@vitest/snapshot": 3.2.4 + "@vitest/spy": 3.2.4 + "@vitest/utils": 3.2.4 + chai: ^5.2.0 + debug: ^4.4.1 + expect-type: ^1.2.1 + magic-string: ^0.30.17 + pathe: ^2.0.3 + picomatch: ^4.0.2 + std-env: ^3.9.0 + tinybench: ^2.9.0 + tinyexec: ^0.3.2 + tinyglobby: ^0.2.14 + tinypool: ^1.1.1 + tinyrainbow: ^2.0.0 + vite: ^5.0.0 || ^6.0.0 || ^7.0.0-0 + vite-node: 3.2.4 + why-is-node-running: ^2.3.0 + peerDependencies: + "@edge-runtime/vm": "*" + "@types/debug": ^4.1.12 + "@types/node": ^18.0.0 || ^20.0.0 || >=22.0.0 + "@vitest/browser": 3.2.4 + "@vitest/ui": 3.2.4 + happy-dom: "*" + jsdom: "*" + peerDependenciesMeta: + "@edge-runtime/vm": + optional: true + "@types/debug": + optional: true + "@types/node": + optional: true + "@vitest/browser": + optional: true + "@vitest/ui": + optional: true + happy-dom: + optional: true + jsdom: + optional: true + bin: + vitest: vitest.mjs + checksum: e9aa14a2c4471c2e0364d1d7032303db8754fac9e5e9ada92fca8ebf61ee78d2c5d4386bff25913940a22ea7d78ab435c8dd85785d681b23e2c489d6c17dd382 + languageName: node + linkType: hard + "void-elements@npm:3.1.0": version: 3.1.0 resolution: "void-elements@npm:3.1.0" @@ -18422,6 +19274,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: ^2.0.0 + stackback: 0.0.2 + bin: + why-is-node-running: cli.js + checksum: 58ebbf406e243ace97083027f0df7ff4c2108baf2595bb29317718ef207cc7a8104e41b711ff65d6fa354f25daa8756b67f2f04931a4fd6ba9d13ae8197496fb + languageName: node + linkType: hard + "widest-line@npm:^4.0.1": version: 4.0.1 resolution: "widest-line@npm:4.0.1"