diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml new file mode 100644 index 0000000..6c8122d --- /dev/null +++ b/.github/workflows/test.yml @@ -0,0 +1,57 @@ +name: Test + +on: + push: + branches: [main] + pull_request: + branches: [main] + workflow_dispatch: + +jobs: + test: + name: Lint, typecheck, build & test + runs-on: ubuntu-latest + + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Set up Node + uses: actions/setup-node@v4 + with: + node-version: 22 + cache: yarn + + - name: Install dependencies + run: yarn install --frozen-lockfile + + # Lint only. The repo is not fully Biome-formatted yet, so `biome ci` + # (which also enforces formatting/import order) is not used here. + - name: Lint + run: npx biome lint . + + - name: Type check + run: npx tsc --noEmit + + - name: Build + run: yarn build + + - name: Cache Playwright browsers + uses: actions/cache@v4 + with: + path: ~/.cache/ms-playwright + key: playwright-${{ runner.os }}-${{ hashFiles('**/yarn.lock') }} + + - name: Install Playwright Chromium + run: npx playwright install --with-deps chromium + + - name: Run tests with coverage + run: yarn coverage + + - name: Upload coverage report + if: ${{ !cancelled() }} + uses: actions/upload-artifact@v4 + with: + name: coverage-report + path: coverage/ + retention-days: 14 diff --git a/.gitignore b/.gitignore index c71fa1c..e078bc0 100755 --- a/.gitignore +++ b/.gitignore @@ -5,6 +5,7 @@ # testing /coverage +**/__screenshots__ # build /dist @@ -21,8 +22,12 @@ npm-debug.log* yarn-debug.log* yarn-error.log* -# storybook -storybook-static - # cache .eslintcache +.gstack/ + +# demo app +/demo/node_modules +/demo/dist +/demo/.vercel +.vercel diff --git a/.npmignore b/.npmignore index 8b415d9..2e63a12 100644 --- a/.npmignore +++ b/.npmignore @@ -1,5 +1,7 @@ /* !/dist -/dist/**/stories !/src -/src/**/stories +/src/**/*.test.* +/src/**/__screenshots__ +/src/jest-dom.d.ts +/demo diff --git a/.storybook/main.js b/.storybook/main.js deleted file mode 100644 index 748ccb6..0000000 --- a/.storybook/main.js +++ /dev/null @@ -1,30 +0,0 @@ -module.exports = { - stories: ['../src/**/*.stories.mdx', '../src/**/*.stories.@(js|jsx|ts|tsx)'], - addons: ['@storybook/addon-links'], - framework: { - name: '@storybook/react-vite', - options: {}, - }, - viteFinal: async (config) => { - config.assetsInclude = config.assetsInclude || []; - config.assetsInclude.push('**/*.md'); - - // Configure esbuild to handle TypeScript properly - config.esbuild = { - target: 'es2020', - loader: 'tsx', - include: /src\/.*\.[jt]sx?$/, - }; - - config.optimizeDeps = config.optimizeDeps || {}; - config.optimizeDeps.esbuildOptions = { - target: 'es2020', - loader: { - '.ts': 'tsx', - '.tsx': 'tsx', - }, - }; - - return config; - }, -}; diff --git a/.storybook/preview.js b/.storybook/preview.js deleted file mode 100644 index b285aac..0000000 --- a/.storybook/preview.js +++ /dev/null @@ -1,3 +0,0 @@ -export const parameters = { - actions: { argTypesRegex: '^on[A-Z].*' }, -}; diff --git a/README.md b/README.md index 7bcc399..95ef358 100755 --- a/README.md +++ b/README.md @@ -5,7 +5,15 @@ A React cron editor built with [material ui](https://material-ui.com/) -For **demo** and **usage** clone repo and run `yarn && yarn dev`. +For a **live demo**, run the demo app in [`demo/`](./demo): + +```bash +yarn demo:install # one-time: install the demo app's deps +yarn demo # http://localhost:5173 +``` + +The demo is a small [TanStack Router](https://tanstack.com/router) + Vite SPA that +imports the component straight from `src/`, so it always reflects the working tree. ![material-ui-cron demo](/docs/material-ui-cron-demo.png) @@ -103,6 +111,72 @@ We are welcoming translation contributions from the community. /> ``` +## Testing + +Tests run on [Vitest](https://vitest.dev/) and are split into two projects: + +- **`unit`** — pure-logic tests that run in Node (cron validation in `utils.ts`, + the cron ⇄ field-atom derivations in `selector.ts`). +- **`browser`** — component tests that render the real `` in a + headless Chromium via [Playwright](https://playwright.dev/). A real browser is + required: MUI's `Autocomplete` triggers an infinite update loop under jsdom. + +Tests live next to the code they cover: + +| File | Project | What it covers | +| --- | --- | --- | +| `src/utils.test.ts` | unit | every cron-part validator + helpers | +| `src/selector.test.ts` | unit | all cron-part derivation atoms and the writer | +| `src/scheduler.browser.test.tsx` | browser | end-to-end `` behaviour | + +### Commands + +```bash +# Install the Chromium browser once (needed for the browser project) +npx playwright install chromium + +yarn test # run the whole suite once (unit + browser) +yarn test:unit # run only the Node unit project +yarn test:browser # run only the Chromium browser project +yarn test:watch # watch mode +yarn coverage # run everything and emit a coverage report +``` + +`yarn coverage` uses the V8 provider and writes a report to `coverage/` +(`text` summary in the console, plus `html` and `json-summary`). + +### Continuous integration + +`.github/workflows/test.yml` runs on pushes and pull requests to `main`. It +lints (`biome lint`), type-checks (`tsc --noEmit`), builds, installs Chromium, +runs `yarn coverage`, and uploads the coverage report as a build artifact. + +## Demo & deployment + +The [`demo/`](./demo) app is deployed to Vercel, which builds a fresh preview for +**every branch and pull request** and comments the live URL on the PR. + +The build is driven by [`vercel.json`](./vercel.json) at the repo root: + +| Step | Command | +| --- | --- | +| Install | `yarn --cwd demo install` | +| Build | `yarn --cwd demo build` | +| Output | `demo/dist` | + +### One-time Vercel setup + +1. Create a Vercel project and link it to this GitHub repository + (Vercel dashboard → *Add New… → Project*, or `npx vercel link`). +2. Leave the **Root Directory** as the repo root — `vercel.json` already points + the build at `demo/`. (The demo aliases the library from `../src`, so the whole + repo must be present at build time.) +3. That's it. Vercel's Git integration produces a Preview Deployment for each push + to a branch / PR and a Production Deployment for `main`. + +No secrets or GitHub Actions workflow are required — preview URLs come from +Vercel's native Git integration. + ## Acknowledgement This library was developed as a part of [Udaan](https://udaan.com/)'s Data diff --git a/debug-storybook.log b/debug-storybook.log deleted file mode 100644 index 6ec10e0..0000000 --- a/debug-storybook.log +++ /dev/null @@ -1,19 +0,0 @@ -[14:02:54.129] [INFO] Checking the health of your Storybook.. -[14:02:54.129] [DEBUG] Getting Storybook info... -[14:02:54.130] [DEBUG] Loading main config... -[14:02:54.256] [DEBUG] Getting stories paths... -[14:02:54.269] [DEBUG] Getting package manager... -[14:02:54.427] [DEBUG] Getting Storybook version... -[14:02:54.686] [WARN] .storybook: 1 issue found -[14:02:54.686] [INFO] You are currently using Storybook 9.1.10 but you have packages which are incompatible with it: - -- @storybook/addon-essentials@8.6.14 which depends on 8.6.14 - Repo: https://github.com/storybookjs/storybook/tree/next/code/addons/essentials - -Please consider updating your packages or contacting the maintainers for compatibility details. - -For more on Storybook 9 compatibility, see the linked GitHub issue: -https://github.com/storybookjs/storybook/issues/30944 -[14:02:54.692] [INFO] Storybook doctor is complete! -[14:02:54.692] [INFO] You can always recheck the health of your project(s) by running: -npx storybook doctor \ No newline at end of file diff --git a/demo/README.md b/demo/README.md new file mode 100644 index 0000000..b7f5706 --- /dev/null +++ b/demo/README.md @@ -0,0 +1,40 @@ +# material-ui-cron · demo + +A small [TanStack Router](https://tanstack.com/router) + [Vite](https://vitejs.dev/) +single-page app that demos the `material-ui-cron` component. + +It imports the library **straight from the repo source** (`../src`) via a Vite +alias, so the demo always reflects the current working tree — no build or +`npm link` step required. + +## Run locally + +```bash +# from the repo root +yarn demo # installs nothing; runs `yarn --cwd demo dev` + +# or directly +cd demo +yarn install +yarn dev # http://localhost:5173 +``` + +Other scripts (run inside `demo/`): + +```bash +yarn build # production build → demo/dist +yarn preview # preview the production build +yarn typecheck # tsc --noEmit +``` + +## Deployment (Vercel) + +Preview deployments for every branch and pull request are produced by Vercel's +native Git integration. The build is configured by `vercel.json` at the repo +root: + +- **Install:** `yarn --cwd demo install` +- **Build:** `yarn --cwd demo build` +- **Output:** `demo/dist` + +See the root `README.md` → *Demo & deployment* for the one-time Vercel setup. diff --git a/demo/index.html b/demo/index.html new file mode 100644 index 0000000..326d3da --- /dev/null +++ b/demo/index.html @@ -0,0 +1,16 @@ + + + + + + + material-ui-cron · demo + + +
+ + + diff --git a/demo/package.json b/demo/package.json new file mode 100644 index 0000000..66dd405 --- /dev/null +++ b/demo/package.json @@ -0,0 +1,33 @@ +{ + "name": "material-ui-cron-demo", + "private": true, + "version": "0.0.0", + "type": "module", + "description": "Live demo app for the material-ui-cron library (TanStack Router + Vite).", + "scripts": { + "dev": "vite", + "build": "vite build", + "preview": "vite preview", + "typecheck": "tsc --noEmit" + }, + "dependencies": { + "@emotion/react": "^11.14.0", + "@emotion/styled": "^11.14.1", + "@mui/icons-material": "^7.3.4", + "@mui/material": "^7.3.4", + "@mui/system": "^7.3.3", + "@tanstack/react-router": "^1", + "cronstrue": "^3.3.0", + "jotai": "^2.15.0", + "react": "^19.2.0", + "react-dom": "^19.2.0" + }, + "devDependencies": { + "@types/node": "^24.7.1", + "@types/react": "^19.2.2", + "@types/react-dom": "^19.2.1", + "@vitejs/plugin-react": "^5.0.4", + "typescript": "^5.9.3", + "vite": "^7.1.9" + } +} diff --git a/demo/src/main.tsx b/demo/src/main.tsx new file mode 100644 index 0000000..1b49921 --- /dev/null +++ b/demo/src/main.tsx @@ -0,0 +1,28 @@ +import CssBaseline from '@mui/material/CssBaseline'; +import { ThemeProvider, createTheme } from '@mui/material/styles'; +import { RouterProvider } from '@tanstack/react-router'; +import { createRoot } from 'react-dom/client'; +import { router } from './router'; + +const theme = createTheme({ + palette: { + mode: 'light', + primary: { main: '#1565c0' }, + }, +}); + +const container = document.getElementById('root'); +if (!container) { + throw new Error('Root container #root not found'); +} + +// NOTE: React.StrictMode is intentionally omitted. The Scheduler keeps its cron +// state in module-level Jotai atoms and resets them in an unmount effect, so +// StrictMode's mount→unmount→remount cycle wipes the initial `cron` prop before +// the first paint (the field would show `0 0 * * *` instead of the passed value). +createRoot(container).render( + + + + , +); diff --git a/demo/src/pages/DemoPage.tsx b/demo/src/pages/DemoPage.tsx new file mode 100644 index 0000000..81247f8 --- /dev/null +++ b/demo/src/pages/DemoPage.tsx @@ -0,0 +1,185 @@ +import GitHubIcon from '@mui/icons-material/GitHub'; +import { + Alert, + AppBar, + Box, + Chip, + Container, + FormControlLabel, + Link, + MenuItem, + Paper, + Stack, + Switch, + TextField, + Toolbar, + Typography, +} from '@mui/material'; +import cronstrue from 'cronstrue'; +import 'cronstrue/locales/zh_CN'; +import { useState } from 'react'; +import Scheduler, { type definedLocales } from 'material-ui-cron'; + +const PRESETS: { label: string; cron: string }[] = [ + { label: 'Every minute', cron: '* * * * *' }, + { label: 'Every 15 minutes', cron: '*/15 * * * *' }, + { label: 'Hourly', cron: '0 * * * *' }, + { label: 'Daily at 09:00', cron: '0 9 * * *' }, + { label: 'Weekdays at 18:30', cron: '30 18 * * 1-5' }, + { label: 'Monthly (1st)', cron: '0 0 1 * *' }, + { label: 'Yearly (Jan 1)', cron: '0 0 1 1 *' }, +]; + +const LOCALES: { value: definedLocales; label: string }[] = [ + { value: 'en', label: 'English (en)' }, + { value: 'zh_CN', label: '中文 (zh_CN)' }, +]; + +function describe(cron: string, locale: definedLocales): string { + try { + return cronstrue.toString(cron, { + locale: locale === 'zh_CN' ? 'zh_CN' : 'en', + throwExceptionOnParseError: true, + }); + } catch { + return '—'; + } +} + +export function DemoPage() { + const [cron, setCron] = useState('0 9 * * *'); + const [cronError, setCronError] = useState(''); + const [isAdmin, setIsAdmin] = useState(true); + const [locale, setLocale] = useState('en'); + + return ( + + + + + material-ui-cron + + + npm + + + + + + + + + + + A React cron editor, built with Material UI + + + Edit the schedule below, try a preset, toggle admin mode, or switch the locale. The + component is rendered straight from this repository's src/. + + + + + + setIsAdmin(e.target.checked)} />} + label="Admin (allow sub-daily frequency)" + /> + setLocale(e.target.value as definedLocales)} + sx={{ minWidth: 180 }} + > + {LOCALES.map((l) => ( + + {l.label} + + ))} + + + + + + + + + Presets + + + {PRESETS.map((p) => ( + setCron(p.cron)} + color={cron === p.cron ? 'primary' : 'default'} + variant={cron === p.cron ? 'filled' : 'outlined'} + /> + ))} + + + + + + Current value + + + {cron} + + + {describe(cron, locale)} + + {cronError ? ( + + {cronError} + + ) : null} + + + + + Read the usage docs → + + + + + ); +} diff --git a/demo/src/router.tsx b/demo/src/router.tsx new file mode 100644 index 0000000..b2d6dac --- /dev/null +++ b/demo/src/router.tsx @@ -0,0 +1,27 @@ +import { + Outlet, + createRootRoute, + createRoute, + createRouter, +} from '@tanstack/react-router'; +import { DemoPage } from './pages/DemoPage'; + +const rootRoute = createRootRoute({ + component: () => , +}); + +const indexRoute = createRoute({ + getParentRoute: () => rootRoute, + path: '/', + component: DemoPage, +}); + +const routeTree = rootRoute.addChildren([indexRoute]); + +export const router = createRouter({ routeTree }); + +declare module '@tanstack/react-router' { + interface Register { + router: typeof router; + } +} diff --git a/demo/tsconfig.json b/demo/tsconfig.json new file mode 100644 index 0000000..49f269e --- /dev/null +++ b/demo/tsconfig.json @@ -0,0 +1,29 @@ +{ + "compilerOptions": { + "target": "ES2020", + "useDefineForClassFields": true, + "lib": ["ES2020", "DOM", "DOM.Iterable"], + "module": "ESNext", + "skipLibCheck": true, + "moduleResolution": "bundler", + "allowImportingTsExtensions": true, + "resolveJsonModule": true, + "isolatedModules": true, + "noEmit": true, + "jsx": "react-jsx", + "strict": true, + "noUnusedLocals": true, + "noUnusedParameters": true, + "noFallthroughCasesInSwitch": true, + "baseUrl": ".", + // For type-checking, resolve the library against its built public types + // (like a real consumer). The Vite *runtime* alias still points at ../src, + // so the dev server and build use live source — run `yarn build` in the repo + // root first if dist/ is missing. This keeps `yarn typecheck` from pulling + // the library internals in under the demo's stricter compiler flags. + "paths": { + "material-ui-cron": ["../dist/index.d.ts"] + } + }, + "include": ["src", "vite.config.ts"] +} diff --git a/demo/vite.config.ts b/demo/vite.config.ts new file mode 100644 index 0000000..484f208 --- /dev/null +++ b/demo/vite.config.ts @@ -0,0 +1,26 @@ +import { resolve } from 'node:path'; +import react from '@vitejs/plugin-react'; +import { defineConfig } from 'vite'; + +// The demo consumes the library straight from source (`../src`) rather than the +// published package. This keeps the demo always in sync with the working tree, +// gives full HMR while editing the library, and needs no build step. +export default defineConfig({ + plugins: [react()], + resolve: { + alias: { + 'material-ui-cron': resolve(__dirname, '../src/index.ts'), + }, + // The library source imports these; make sure a single copy is used so React + // hooks and Emotion's cache behave correctly. + dedupe: [ + 'react', + 'react-dom', + 'jotai', + '@emotion/react', + '@emotion/styled', + '@mui/material', + '@mui/system', + ], + }, +}); diff --git a/demo/yarn.lock b/demo/yarn.lock new file mode 100644 index 0000000..23c6475 --- /dev/null +++ b/demo/yarn.lock @@ -0,0 +1,1292 @@ +# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. +# yarn lockfile v1 + + +"@babel/code-frame@^7.0.0", "@babel/code-frame@^7.29.7": + version "7.29.7" + resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.29.7.tgz#f2fbbfea87c44a21590ec515b778b2c26d8866e7" + integrity sha512-Aup7aUOfpbAUg2ROOJN6Iw5f9DMBlzu0mIkm/malLQFN/YQgO48wCj0Kxa3sEHJvPVFg7siR+qRInwXd2qhQKw== + dependencies: + "@babel/helper-validator-identifier" "^7.29.7" + js-tokens "^4.0.0" + picocolors "^1.1.1" + +"@babel/compat-data@^7.29.7": + version "7.29.7" + resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.29.7.tgz#6f0237f0f36d2e51c0570a636faed9d2d0efe629" + integrity sha512-locTkQyKvwIEgBzVrn8693ebc97F2U8ZHjbXwDXJ5Fn2TCpNwTlKcaKLkdHop5c/icOFE7qt7Q9JC5hnKNa6Gg== + +"@babel/core@^7.29.0": + version "7.29.7" + resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.29.7.tgz#80c10b17248082968b57a857b91640971f2070f7" + integrity sha512-RgHBCvtjbOK2gXSNBNIkNoEc9qoVEtau3hj8gEqKQuL3HZAibKarWFEI3Lfm6EYKkLalOh8eSrj9b+ch9H/VBA== + dependencies: + "@babel/code-frame" "^7.29.7" + "@babel/generator" "^7.29.7" + "@babel/helper-compilation-targets" "^7.29.7" + "@babel/helper-module-transforms" "^7.29.7" + "@babel/helpers" "^7.29.7" + "@babel/parser" "^7.29.7" + "@babel/template" "^7.29.7" + "@babel/traverse" "^7.29.7" + "@babel/types" "^7.29.7" + "@jridgewell/remapping" "^2.3.5" + convert-source-map "^2.0.0" + debug "^4.1.0" + gensync "^1.0.0-beta.2" + json5 "^2.2.3" + semver "^6.3.1" + +"@babel/generator@^7.29.7": + version "7.29.7" + resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.29.7.tgz#cca0b8827e6bcf3ba176788e7f3b180ad6db2fa3" + integrity sha512-DkXD5OJQaAQIdZ1bt3UZdEnHAn9Imd3IVBdX03UFe+ony9Ojw5pzr9YVKGDY1jt+Gcn/FnGkNf8r+Vj5NOJWtQ== + dependencies: + "@babel/parser" "^7.29.7" + "@babel/types" "^7.29.7" + "@jridgewell/gen-mapping" "^0.3.12" + "@jridgewell/trace-mapping" "^0.3.28" + jsesc "^3.0.2" + +"@babel/helper-compilation-targets@^7.29.7": + version "7.29.7" + resolved "https://registry.yarnpkg.com/@babel/helper-compilation-targets/-/helper-compilation-targets-7.29.7.tgz#7a1def704302401c47f64fa85589e974ae217042" + integrity sha512-wem6WaBj4NaVYVdNhLPPVacES6ZJ+KBBfSkTMD3YZxbP3rm3Di85tJU5ljaUNhaOynt+Aj0xruhYuzQBt8n71g== + dependencies: + "@babel/compat-data" "^7.29.7" + "@babel/helper-validator-option" "^7.29.7" + browserslist "^4.24.0" + lru-cache "^5.1.1" + semver "^6.3.1" + +"@babel/helper-globals@^7.29.7": + version "7.29.7" + resolved "https://registry.yarnpkg.com/@babel/helper-globals/-/helper-globals-7.29.7.tgz#f04a96fbd8473241b1079243f5b3f03a3010ab7b" + integrity sha512-3nQVUAtvkKH9zahfWgw96Jc/uFOmjACE1kQz82E2lqWmHBgjzbNlsC22nuQTfahmWeQtTq5nQ/4Nnd2A1wj4zA== + +"@babel/helper-module-imports@^7.16.7", "@babel/helper-module-imports@^7.29.7": + version "7.29.7" + resolved "https://registry.yarnpkg.com/@babel/helper-module-imports/-/helper-module-imports-7.29.7.tgz#ef25048a518e828d7393fac5882ddd73921d7396" + integrity sha512-ejHwrQQYcm9xnTivShn2IDOlIzInN34AXskvq9QicvCtEzq1Vzclu/tKF8Jq1Cg8JG2GL6/EmjgsCT7lXepE3g== + dependencies: + "@babel/traverse" "^7.29.7" + "@babel/types" "^7.29.7" + +"@babel/helper-module-transforms@^7.29.7": + version "7.29.7" + resolved "https://registry.yarnpkg.com/@babel/helper-module-transforms/-/helper-module-transforms-7.29.7.tgz#b062747a5997ba138637201328bbff77960574ae" + integrity sha512-UPUVSyXbOh627KiCIGQSgwWzGeBKLkaJ9PJEdrngIwMSzxLR4jS4+f1f1jb7VzBbg8nFLaYotvVPFCTqdrmTAg== + dependencies: + "@babel/helper-module-imports" "^7.29.7" + "@babel/helper-validator-identifier" "^7.29.7" + "@babel/traverse" "^7.29.7" + +"@babel/helper-plugin-utils@^7.29.7": + version "7.29.7" + resolved "https://registry.yarnpkg.com/@babel/helper-plugin-utils/-/helper-plugin-utils-7.29.7.tgz#c0a0766f1a13617d8a17407d7ab8f9d486225ea4" + integrity sha512-G7sHYigPY17oO5SYWnfD/0MTBwVR781S/JI643e/JhUYgVgWE/61SoW3NH9KWUKyKq5LVh3npif99Wkt6j86Jw== + +"@babel/helper-string-parser@^7.29.7": + version "7.29.7" + resolved "https://registry.yarnpkg.com/@babel/helper-string-parser/-/helper-string-parser-7.29.7.tgz#7f0871d99824d23137d60f86fcf6130fd5a1b51f" + integrity sha512-Pb5ijPrZ89GDH8223L4UP8i6QApWxs04RbPQJTeWDV0/keR2E36MeKnyr6LYmUUvqRRI+Iv87SuF1W6ErINzYw== + +"@babel/helper-validator-identifier@^7.29.7": + version "7.29.7" + resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.29.7.tgz#bd87084ced0c796ec46bda492de6e83d29e89fc2" + integrity sha512-qehxGkRj55h/ff8EMaJ+cYhyaKlHIxqYDn682wQD7RNp9UujOQsHog2uS0r2vzr4pW+sXf90NeeayjcNaX3fFg== + +"@babel/helper-validator-option@^7.29.7": + version "7.29.7" + resolved "https://registry.yarnpkg.com/@babel/helper-validator-option/-/helper-validator-option-7.29.7.tgz#cf315be940213b354eb4abcc0bd01ebe3f73bc2a" + integrity sha512-N9ZErrD+yW5geCDtBqnOoxmR8+tNKiGuxKlDpuJxfsqpa2dFcexaziGAE/qoHLiDDreVNMupxGmSoNlyvsA3gw== + +"@babel/helpers@^7.29.7": + version "7.29.7" + resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.29.7.tgz#45abfde7548997e34376c3e69feb475cffb4a607" + integrity sha512-1k2lAGRMfHTcwuNYcCNUmaUffmQv8KWMfh2iJUUeRlwlwH4FdNG7mfPI10NPfLHJFThE4Tyr4mv7kTNZOiPuBg== + dependencies: + "@babel/template" "^7.29.7" + "@babel/types" "^7.29.7" + +"@babel/parser@^7.1.0", "@babel/parser@^7.20.7", "@babel/parser@^7.29.7": + version "7.29.7" + resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.29.7.tgz#837b87387cbf5ec5530cb634b3c622f68edb9334" + integrity sha512-hnORnjP/1P/zFEndoeX+n+t1RwWRJiJpM/jO7FW32Kn9r5+sJB2JWOdYo4L6k78j15eCwY3Gm/7364B1EMwtNg== + dependencies: + "@babel/types" "^7.29.7" + +"@babel/plugin-transform-react-jsx-self@^7.27.1": + version "7.29.7" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-jsx-self/-/plugin-transform-react-jsx-self-7.29.7.tgz#c24424527858220624fd59a5b1eab4fa413c803a" + integrity sha512-TL0hMc9xzy86VD31nUiwzd5otRAcyEPcsegCxolO0PvcXuH1v0kECe/UIznYFihpkvU5wg/jk4v0TTEFfm53fw== + dependencies: + "@babel/helper-plugin-utils" "^7.29.7" + +"@babel/plugin-transform-react-jsx-source@^7.27.1": + version "7.29.7" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-jsx-source/-/plugin-transform-react-jsx-source-7.29.7.tgz#5cf25a3689906b58e2f0a2f2b374789e6627b15f" + integrity sha512-06IyK09H3wi4cGbhDBwp5gUGo0IKtnYa8tyTiephirPCK6fbobVGiXMMI5zLQ4aKEYP3wZ3ArU44o+8KMrSG/Q== + dependencies: + "@babel/helper-plugin-utils" "^7.29.7" + +"@babel/runtime@^7.12.5", "@babel/runtime@^7.18.3", "@babel/runtime@^7.28.6", "@babel/runtime@^7.5.5", "@babel/runtime@^7.8.7": + version "7.29.7" + resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.29.7.tgz#12022450c45a4da6d8d8287b18a4ff2ddb23f768" + integrity sha512-Nq8OhGWiZIZGV6hLHoyAKLLcJihP/xFeBMGJoUrxTX2psI8dCifzLhZISFb+VWS3wFMRDmCGw5R+dOySCqPLhw== + +"@babel/template@^7.29.7": + version "7.29.7" + resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.29.7.tgz#4d9d4004f645cdd304de958c725162784ecac700" + integrity sha512-puq+Gf35oI24FeN11LkoUQFqv9uwNeWpxXZi/Ji3rRIoKAzKnxRaZ+Gkj0vKS9ZCiTESfng1N9LyOyXvo+m+Gg== + dependencies: + "@babel/code-frame" "^7.29.7" + "@babel/parser" "^7.29.7" + "@babel/types" "^7.29.7" + +"@babel/traverse@^7.29.7": + version "7.29.7" + resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.29.7.tgz#c47b07a41b95da0907d026b5dd894d98de7d2f2d" + integrity sha512-EhlfNQtZ+NK22w5BM61ciuiq1m58ed33Wr1Xan//ZRTy6hgjnwyCffRYwzsGXdASJSUJ1guZILsErh1eQcl+zw== + dependencies: + "@babel/code-frame" "^7.29.7" + "@babel/generator" "^7.29.7" + "@babel/helper-globals" "^7.29.7" + "@babel/parser" "^7.29.7" + "@babel/template" "^7.29.7" + "@babel/types" "^7.29.7" + debug "^4.3.1" + +"@babel/types@^7.0.0", "@babel/types@^7.20.7", "@babel/types@^7.28.2", "@babel/types@^7.29.7": + version "7.29.7" + resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.29.7.tgz#8005e31d82712ee7adaef6e23c63b71a62770a92" + integrity sha512-4zBIxpPzowiZpusoFkyGVwakdRJUyuH5PxQ/PrqghfdFWWasvnCdPfQXHrenDai+gyLARulZjZowCOj6fjT4pA== + dependencies: + "@babel/helper-string-parser" "^7.29.7" + "@babel/helper-validator-identifier" "^7.29.7" + +"@emotion/babel-plugin@^11.13.5": + version "11.13.5" + resolved "https://registry.yarnpkg.com/@emotion/babel-plugin/-/babel-plugin-11.13.5.tgz#eab8d65dbded74e0ecfd28dc218e75607c4e7bc0" + integrity sha512-pxHCpT2ex+0q+HH91/zsdHkw/lXd468DIN2zvfvLtPKLLMo6gQj7oLObq8PhkrxOZb/gGCq03S3Z7PDhS8pduQ== + dependencies: + "@babel/helper-module-imports" "^7.16.7" + "@babel/runtime" "^7.18.3" + "@emotion/hash" "^0.9.2" + "@emotion/memoize" "^0.9.0" + "@emotion/serialize" "^1.3.3" + babel-plugin-macros "^3.1.0" + convert-source-map "^1.5.0" + escape-string-regexp "^4.0.0" + find-root "^1.1.0" + source-map "^0.5.7" + stylis "4.2.0" + +"@emotion/cache@^11.14.0": + version "11.14.0" + resolved "https://registry.yarnpkg.com/@emotion/cache/-/cache-11.14.0.tgz#ee44b26986eeb93c8be82bb92f1f7a9b21b2ed76" + integrity sha512-L/B1lc/TViYk4DcpGxtAVbx0ZyiKM5ktoIyafGkH6zg/tj+mA+NE//aPYKG0k8kCHSHVJrpLpcAlOBEXQ3SavA== + dependencies: + "@emotion/memoize" "^0.9.0" + "@emotion/sheet" "^1.4.0" + "@emotion/utils" "^1.4.2" + "@emotion/weak-memoize" "^0.4.0" + stylis "4.2.0" + +"@emotion/hash@^0.9.2": + version "0.9.2" + resolved "https://registry.yarnpkg.com/@emotion/hash/-/hash-0.9.2.tgz#ff9221b9f58b4dfe61e619a7788734bd63f6898b" + integrity sha512-MyqliTZGuOm3+5ZRSaaBGP3USLw6+EGykkwZns2EPC5g8jJ4z9OrdZY9apkl3+UP9+sdz76YYkwCKP5gh8iY3g== + +"@emotion/is-prop-valid@^1.3.0": + version "1.4.0" + resolved "https://registry.yarnpkg.com/@emotion/is-prop-valid/-/is-prop-valid-1.4.0.tgz#e9ad47adff0b5c94c72db3669ce46de33edf28c0" + integrity sha512-QgD4fyscGcbbKwJmqNvUMSE02OsHUa+lAWKdEUIJKgqe5IwRSKd7+KhibEWdaKwgjLj0DRSHA9biAIqGBk05lw== + dependencies: + "@emotion/memoize" "^0.9.0" + +"@emotion/memoize@^0.9.0": + version "0.9.0" + resolved "https://registry.yarnpkg.com/@emotion/memoize/-/memoize-0.9.0.tgz#745969d649977776b43fc7648c556aaa462b4102" + integrity sha512-30FAj7/EoJ5mwVPOWhAyCX+FPfMDrVecJAM+Iw9NRoSl4BBAQeqj4cApHHUXOVvIPgLVDsCFoz/hGD+5QQD1GQ== + +"@emotion/react@^11.14.0": + version "11.14.0" + resolved "https://registry.yarnpkg.com/@emotion/react/-/react-11.14.0.tgz#cfaae35ebc67dd9ef4ea2e9acc6cd29e157dd05d" + integrity sha512-O000MLDBDdk/EohJPFUqvnp4qnHeYkVP5B0xEG0D/L7cOKP9kefu2DXn8dj74cQfsEzUqh+sr1RzFqiL1o+PpA== + dependencies: + "@babel/runtime" "^7.18.3" + "@emotion/babel-plugin" "^11.13.5" + "@emotion/cache" "^11.14.0" + "@emotion/serialize" "^1.3.3" + "@emotion/use-insertion-effect-with-fallbacks" "^1.2.0" + "@emotion/utils" "^1.4.2" + "@emotion/weak-memoize" "^0.4.0" + hoist-non-react-statics "^3.3.1" + +"@emotion/serialize@^1.3.3": + version "1.3.3" + resolved "https://registry.yarnpkg.com/@emotion/serialize/-/serialize-1.3.3.tgz#d291531005f17d704d0463a032fe679f376509e8" + integrity sha512-EISGqt7sSNWHGI76hC7x1CksiXPahbxEOrC5RjmFRJTqLyEK9/9hZvBbiYn70dw4wuwMKiEMCUlR6ZXTSWQqxA== + dependencies: + "@emotion/hash" "^0.9.2" + "@emotion/memoize" "^0.9.0" + "@emotion/unitless" "^0.10.0" + "@emotion/utils" "^1.4.2" + csstype "^3.0.2" + +"@emotion/sheet@^1.4.0": + version "1.4.0" + resolved "https://registry.yarnpkg.com/@emotion/sheet/-/sheet-1.4.0.tgz#c9299c34d248bc26e82563735f78953d2efca83c" + integrity sha512-fTBW9/8r2w3dXWYM4HCB1Rdp8NLibOw2+XELH5m5+AkWiL/KqYX6dc0kKYlaYyKjrQ6ds33MCdMPEwgs2z1rqg== + +"@emotion/styled@^11.14.1": + version "11.14.1" + resolved "https://registry.yarnpkg.com/@emotion/styled/-/styled-11.14.1.tgz#8c34bed2948e83e1980370305614c20955aacd1c" + integrity sha512-qEEJt42DuToa3gurlH4Qqc1kVpNq8wO8cJtDzU46TjlzWjDlsVyevtYCRijVq3SrHsROS+gVQ8Fnea108GnKzw== + dependencies: + "@babel/runtime" "^7.18.3" + "@emotion/babel-plugin" "^11.13.5" + "@emotion/is-prop-valid" "^1.3.0" + "@emotion/serialize" "^1.3.3" + "@emotion/use-insertion-effect-with-fallbacks" "^1.2.0" + "@emotion/utils" "^1.4.2" + +"@emotion/unitless@^0.10.0": + version "0.10.0" + resolved "https://registry.yarnpkg.com/@emotion/unitless/-/unitless-0.10.0.tgz#2af2f7c7e5150f497bdabd848ce7b218a27cf745" + integrity sha512-dFoMUuQA20zvtVTuxZww6OHoJYgrzfKM1t52mVySDJnMSEa08ruEvdYQbhvyu6soU+NeLVd3yKfTfT0NeV6qGg== + +"@emotion/use-insertion-effect-with-fallbacks@^1.2.0": + version "1.2.0" + resolved "https://registry.yarnpkg.com/@emotion/use-insertion-effect-with-fallbacks/-/use-insertion-effect-with-fallbacks-1.2.0.tgz#8a8cb77b590e09affb960f4ff1e9a89e532738bf" + integrity sha512-yJMtVdH59sxi/aVJBpk9FQq+OR8ll5GT8oWd57UpeaKEVGab41JWaCFA7FRLoMLloOZF/c/wsPoe+bfGmRKgDg== + +"@emotion/utils@^1.4.2": + version "1.4.2" + resolved "https://registry.yarnpkg.com/@emotion/utils/-/utils-1.4.2.tgz#6df6c45881fcb1c412d6688a311a98b7f59c1b52" + integrity sha512-3vLclRofFziIa3J2wDh9jjbkUz9qk5Vi3IZ/FSTKViB0k+ef0fPV7dYrUIugbgupYDx7v9ud/SjrtEP8Y4xLoA== + +"@emotion/weak-memoize@^0.4.0": + version "0.4.0" + resolved "https://registry.yarnpkg.com/@emotion/weak-memoize/-/weak-memoize-0.4.0.tgz#5e13fac887f08c44f76b0ccaf3370eb00fec9bb6" + integrity sha512-snKqtPW01tN0ui7yu9rGv69aJXr/a/Ywvl11sUjNtEcRc+ng/mQriFL0wLXMef74iHa/EkftbDzU9F8iFbH+zg== + +"@esbuild/aix-ppc64@0.27.7": + version "0.27.7" + resolved "https://registry.yarnpkg.com/@esbuild/aix-ppc64/-/aix-ppc64-0.27.7.tgz#82b74f92aa78d720b714162939fb248c90addf53" + integrity sha512-EKX3Qwmhz1eMdEJokhALr0YiD0lhQNwDqkPYyPhiSwKrh7/4KRjQc04sZ8db+5DVVnZ1LmbNDI1uAMPEUBnQPg== + +"@esbuild/android-arm64@0.27.7": + version "0.27.7" + resolved "https://registry.yarnpkg.com/@esbuild/android-arm64/-/android-arm64-0.27.7.tgz#f78cb8a3121fc205a53285adb24972db385d185d" + integrity sha512-62dPZHpIXzvChfvfLJow3q5dDtiNMkwiRzPylSCfriLvZeq0a1bWChrGx/BbUbPwOrsWKMn8idSllklzBy+dgQ== + +"@esbuild/android-arm@0.27.7": + version "0.27.7" + resolved "https://registry.yarnpkg.com/@esbuild/android-arm/-/android-arm-0.27.7.tgz#593e10a1450bbfcac6cb321f61f468453bac209d" + integrity sha512-jbPXvB4Yj2yBV7HUfE2KHe4GJX51QplCN1pGbYjvsyCZbQmies29EoJbkEc+vYuU5o45AfQn37vZlyXy4YJ8RQ== + +"@esbuild/android-x64@0.27.7": + version "0.27.7" + resolved "https://registry.yarnpkg.com/@esbuild/android-x64/-/android-x64-0.27.7.tgz#453143d073326033d2d22caf9e48de4bae274b07" + integrity sha512-x5VpMODneVDb70PYV2VQOmIUUiBtY3D3mPBG8NxVk5CogneYhkR7MmM3yR/uMdITLrC1ml/NV1rj4bMJuy9MCg== + +"@esbuild/darwin-arm64@0.27.7": + version "0.27.7" + resolved "https://registry.yarnpkg.com/@esbuild/darwin-arm64/-/darwin-arm64-0.27.7.tgz#6f23000fb9b40b7e04b7d0606c0693bd0632f322" + integrity sha512-5lckdqeuBPlKUwvoCXIgI2D9/ABmPq3Rdp7IfL70393YgaASt7tbju3Ac+ePVi3KDH6N2RqePfHnXkaDtY9fkw== + +"@esbuild/darwin-x64@0.27.7": + version "0.27.7" + resolved "https://registry.yarnpkg.com/@esbuild/darwin-x64/-/darwin-x64-0.27.7.tgz#27393dd18bb1263c663979c5f1576e00c2d024be" + integrity sha512-rYnXrKcXuT7Z+WL5K980jVFdvVKhCHhUwid+dDYQpH+qu+TefcomiMAJpIiC2EM3Rjtq0sO3StMV/+3w3MyyqQ== + +"@esbuild/freebsd-arm64@0.27.7": + version "0.27.7" + resolved "https://registry.yarnpkg.com/@esbuild/freebsd-arm64/-/freebsd-arm64-0.27.7.tgz#22e4638fa502d1c0027077324c97640e3adf3a62" + integrity sha512-B48PqeCsEgOtzME2GbNM2roU29AMTuOIN91dsMO30t+Ydis3z/3Ngoj5hhnsOSSwNzS+6JppqWsuhTp6E82l2w== + +"@esbuild/freebsd-x64@0.27.7": + version "0.27.7" + resolved "https://registry.yarnpkg.com/@esbuild/freebsd-x64/-/freebsd-x64-0.27.7.tgz#9224b8e4fea924ce2194e3efc3e9aebf822192d6" + integrity sha512-jOBDK5XEjA4m5IJK3bpAQF9/Lelu/Z9ZcdhTRLf4cajlB+8VEhFFRjWgfy3M1O4rO2GQ/b2dLwCUGpiF/eATNQ== + +"@esbuild/linux-arm64@0.27.7": + version "0.27.7" + resolved "https://registry.yarnpkg.com/@esbuild/linux-arm64/-/linux-arm64-0.27.7.tgz#4f5d1c27527d817b35684ae21419e57c2bda0966" + integrity sha512-RZPHBoxXuNnPQO9rvjh5jdkRmVizktkT7TCDkDmQ0W2SwHInKCAV95GRuvdSvA7w4VMwfCjUiPwDi0ZO6Nfe9A== + +"@esbuild/linux-arm@0.27.7": + version "0.27.7" + resolved "https://registry.yarnpkg.com/@esbuild/linux-arm/-/linux-arm-0.27.7.tgz#b9e9d070c8c1c0449cf12b20eac37d70a4595921" + integrity sha512-RkT/YXYBTSULo3+af8Ib0ykH8u2MBh57o7q/DAs3lTJlyVQkgQvlrPTnjIzzRPQyavxtPtfg0EopvDyIt0j1rA== + +"@esbuild/linux-ia32@0.27.7": + version "0.27.7" + resolved "https://registry.yarnpkg.com/@esbuild/linux-ia32/-/linux-ia32-0.27.7.tgz#3f80fb696aa96051a94047f35c85b08b21c36f9e" + integrity sha512-GA48aKNkyQDbd3KtkplYWT102C5sn/EZTY4XROkxONgruHPU72l+gW+FfF8tf2cFjeHaRbWpOYa/uRBz/Xq1Pg== + +"@esbuild/linux-loong64@0.27.7": + version "0.27.7" + resolved "https://registry.yarnpkg.com/@esbuild/linux-loong64/-/linux-loong64-0.27.7.tgz#9be1f2c28210b13ebb4156221bba356fe1675205" + integrity sha512-a4POruNM2oWsD4WKvBSEKGIiWQF8fZOAsycHOt6JBpZ+JN2n2JH9WAv56SOyu9X5IqAjqSIPTaJkqN8F7XOQ5Q== + +"@esbuild/linux-mips64el@0.27.7": + version "0.27.7" + resolved "https://registry.yarnpkg.com/@esbuild/linux-mips64el/-/linux-mips64el-0.27.7.tgz#4ab5ee67a3dfcbcb5e8fd7883dae6e735b1163b8" + integrity sha512-KabT5I6StirGfIz0FMgl1I+R1H73Gp0ofL9A3nG3i/cYFJzKHhouBV5VWK1CSgKvVaG4q1RNpCTR2LuTVB3fIw== + +"@esbuild/linux-ppc64@0.27.7": + version "0.27.7" + resolved "https://registry.yarnpkg.com/@esbuild/linux-ppc64/-/linux-ppc64-0.27.7.tgz#dac78c689f6499459c4321e5c15032c12307e7ea" + integrity sha512-gRsL4x6wsGHGRqhtI+ifpN/vpOFTQtnbsupUF5R5YTAg+y/lKelYR1hXbnBdzDjGbMYjVJLJTd2OFmMewAgwlQ== + +"@esbuild/linux-riscv64@0.27.7": + version "0.27.7" + resolved "https://registry.yarnpkg.com/@esbuild/linux-riscv64/-/linux-riscv64-0.27.7.tgz#050f7d3b355c3a98308e935bc4d6325da91b0027" + integrity sha512-hL25LbxO1QOngGzu2U5xeXtxXcW+/GvMN3ejANqXkxZ/opySAZMrc+9LY/WyjAan41unrR3YrmtTsUpwT66InQ== + +"@esbuild/linux-s390x@0.27.7": + version "0.27.7" + resolved "https://registry.yarnpkg.com/@esbuild/linux-s390x/-/linux-s390x-0.27.7.tgz#d61f715ce61d43fe5844ad0d8f463f88cbe4fef6" + integrity sha512-2k8go8Ycu1Kb46vEelhu1vqEP+UeRVj2zY1pSuPdgvbd5ykAw82Lrro28vXUrRmzEsUV0NzCf54yARIK8r0fdw== + +"@esbuild/linux-x64@0.27.7": + version "0.27.7" + resolved "https://registry.yarnpkg.com/@esbuild/linux-x64/-/linux-x64-0.27.7.tgz#ca8e1aa478fc8209257bf3ac8f79c4dc2982f32a" + integrity sha512-hzznmADPt+OmsYzw1EE33ccA+HPdIqiCRq7cQeL1Jlq2gb1+OyWBkMCrYGBJ+sxVzve2ZJEVeePbLM2iEIZSxA== + +"@esbuild/netbsd-arm64@0.27.7": + version "0.27.7" + resolved "https://registry.yarnpkg.com/@esbuild/netbsd-arm64/-/netbsd-arm64-0.27.7.tgz#1650f2c1b948deeb3ef948f2fc30614723c09690" + integrity sha512-b6pqtrQdigZBwZxAn1UpazEisvwaIDvdbMbmrly7cDTMFnw/+3lVxxCTGOrkPVnsYIosJJXAsILG9XcQS+Yu6w== + +"@esbuild/netbsd-x64@0.27.7": + version "0.27.7" + resolved "https://registry.yarnpkg.com/@esbuild/netbsd-x64/-/netbsd-x64-0.27.7.tgz#65772ab342c4b3319bf0705a211050aac1b6e320" + integrity sha512-OfatkLojr6U+WN5EDYuoQhtM+1xco+/6FSzJJnuWiUw5eVcicbyK3dq5EeV/QHT1uy6GoDhGbFpprUiHUYggrw== + +"@esbuild/openbsd-arm64@0.27.7": + version "0.27.7" + resolved "https://registry.yarnpkg.com/@esbuild/openbsd-arm64/-/openbsd-arm64-0.27.7.tgz#37ed7cfa66549d7955852fce37d0c3de4e715ea1" + integrity sha512-AFuojMQTxAz75Fo8idVcqoQWEHIXFRbOc1TrVcFSgCZtQfSdc1RXgB3tjOn/krRHENUB4j00bfGjyl2mJrU37A== + +"@esbuild/openbsd-x64@0.27.7": + version "0.27.7" + resolved "https://registry.yarnpkg.com/@esbuild/openbsd-x64/-/openbsd-x64-0.27.7.tgz#01bf3d385855ef50cb33db7c4b52f957c34cd179" + integrity sha512-+A1NJmfM8WNDv5CLVQYJ5PshuRm/4cI6WMZRg1by1GwPIQPCTs1GLEUHwiiQGT5zDdyLiRM/l1G0Pv54gvtKIg== + +"@esbuild/openharmony-arm64@0.27.7": + version "0.27.7" + resolved "https://registry.yarnpkg.com/@esbuild/openharmony-arm64/-/openharmony-arm64-0.27.7.tgz#6c1f94b34086599aabda4eac8f638294b9877410" + integrity sha512-+KrvYb/C8zA9CU/g0sR6w2RBw7IGc5J2BPnc3dYc5VJxHCSF1yNMxTV5LQ7GuKteQXZtspjFbiuW5/dOj7H4Yw== + +"@esbuild/sunos-x64@0.27.7": + version "0.27.7" + resolved "https://registry.yarnpkg.com/@esbuild/sunos-x64/-/sunos-x64-0.27.7.tgz#4b0dd17ae0a6941d2d0fd35a906392517071a90d" + integrity sha512-ikktIhFBzQNt/QDyOL580ti9+5mL/YZeUPKU2ivGtGjdTYoqz6jObj6nOMfhASpS4GU4Q/Clh1QtxWAvcYKamA== + +"@esbuild/win32-arm64@0.27.7": + version "0.27.7" + resolved "https://registry.yarnpkg.com/@esbuild/win32-arm64/-/win32-arm64-0.27.7.tgz#34193ab5565d6ff68ca928ac04be75102ccb2e77" + integrity sha512-7yRhbHvPqSpRUV7Q20VuDwbjW5kIMwTHpptuUzV+AA46kiPze5Z7qgt6CLCK3pWFrHeNfDd1VKgyP4O+ng17CA== + +"@esbuild/win32-ia32@0.27.7": + version "0.27.7" + resolved "https://registry.yarnpkg.com/@esbuild/win32-ia32/-/win32-ia32-0.27.7.tgz#eb67f0e4482515d8c1894ede631c327a4da9fc4d" + integrity sha512-SmwKXe6VHIyZYbBLJrhOoCJRB/Z1tckzmgTLfFYOfpMAx63BJEaL9ExI8x7v0oAO3Zh6D/Oi1gVxEYr5oUCFhw== + +"@esbuild/win32-x64@0.27.7": + version "0.27.7" + resolved "https://registry.yarnpkg.com/@esbuild/win32-x64/-/win32-x64-0.27.7.tgz#8fe30b3088b89b4873c3a6cc87597ae3920c0a8b" + integrity sha512-56hiAJPhwQ1R4i+21FVF7V8kSD5zZTdHcVuRFMW0hn753vVfQN8xlx4uOPT4xoGH0Z/oVATuR82AiqSTDIpaHg== + +"@jridgewell/gen-mapping@^0.3.12", "@jridgewell/gen-mapping@^0.3.5": + version "0.3.13" + resolved "https://registry.yarnpkg.com/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz#6342a19f44347518c93e43b1ac69deb3c4656a1f" + integrity sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA== + dependencies: + "@jridgewell/sourcemap-codec" "^1.5.0" + "@jridgewell/trace-mapping" "^0.3.24" + +"@jridgewell/remapping@^2.3.5": + version "2.3.5" + resolved "https://registry.yarnpkg.com/@jridgewell/remapping/-/remapping-2.3.5.tgz#375c476d1972947851ba1e15ae8f123047445aa1" + integrity sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ== + dependencies: + "@jridgewell/gen-mapping" "^0.3.5" + "@jridgewell/trace-mapping" "^0.3.24" + +"@jridgewell/resolve-uri@^3.1.0": + version "3.1.2" + resolved "https://registry.yarnpkg.com/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz#7a0ee601f60f99a20c7c7c5ff0c80388c1189bd6" + integrity sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw== + +"@jridgewell/sourcemap-codec@^1.4.14", "@jridgewell/sourcemap-codec@^1.5.0": + version "1.5.5" + resolved "https://registry.yarnpkg.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz#6912b00d2c631c0d15ce1a7ab57cd657f2a8f8ba" + integrity sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og== + +"@jridgewell/trace-mapping@^0.3.24", "@jridgewell/trace-mapping@^0.3.28": + version "0.3.31" + resolved "https://registry.yarnpkg.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz#db15d6781c931f3a251a3dac39501c98a6082fd0" + integrity sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw== + dependencies: + "@jridgewell/resolve-uri" "^3.1.0" + "@jridgewell/sourcemap-codec" "^1.4.14" + +"@mui/core-downloads-tracker@^7.3.11": + version "7.3.11" + resolved "https://registry.yarnpkg.com/@mui/core-downloads-tracker/-/core-downloads-tracker-7.3.11.tgz#93251cf192641b9582641991feea60149159902c" + integrity sha512-a7I/b/nBTdXYz2cOSlEmkQ9WWE1x8FHpqMhFPp+Y1VPFxcOw91G5ELOHARQAGSPy5V+UCgJua6K/1x70bAtQPw== + +"@mui/icons-material@^7.3.4": + version "7.3.11" + resolved "https://registry.yarnpkg.com/@mui/icons-material/-/icons-material-7.3.11.tgz#dfea40829d1ca96c074c85093fdfe0de170af5ad" + integrity sha512-+hz5ilwHZ3djd5es3sCErLioqe/NhZcYTsV/TNXZAMdJdb23F4xzJjqnnZdnurc3S1+ietcssRNqieOhPQLZ7Q== + dependencies: + "@babel/runtime" "^7.28.6" + +"@mui/material@^7.3.4": + version "7.3.11" + resolved "https://registry.yarnpkg.com/@mui/material/-/material-7.3.11.tgz#aa6e0640bb29bd01e6c353ac3436fa0b20e5a36b" + integrity sha512-yq8bPc3LxOwKRWpcjRgDkYFmpM6aKlARfESTmOQcvLYFeJwtHte2tw6hJDrb8sk8wcvpDprHEHVaoUU0MslIkw== + dependencies: + "@babel/runtime" "^7.28.6" + "@mui/core-downloads-tracker" "^7.3.11" + "@mui/system" "^7.3.11" + "@mui/types" "^7.4.12" + "@mui/utils" "^7.3.11" + "@popperjs/core" "^2.11.8" + "@types/react-transition-group" "^4.4.12" + clsx "^2.1.1" + csstype "^3.2.3" + prop-types "^15.8.1" + react-is "^19.2.3" + react-transition-group "^4.4.5" + +"@mui/private-theming@^7.3.11": + version "7.3.11" + resolved "https://registry.yarnpkg.com/@mui/private-theming/-/private-theming-7.3.11.tgz#96d4cde586624916816f5a97fef3c808cf562fb0" + integrity sha512-9B+YKms0fRHbNrqp9tOT/DNbNnU5gyvJ1o3qAGXfq8GmZcbJnE3At9x07Zr/o0pkhzg4aDdwXVqe4+AcgtOCPA== + dependencies: + "@babel/runtime" "^7.28.6" + "@mui/utils" "^7.3.11" + prop-types "^15.8.1" + +"@mui/styled-engine@^7.3.10": + version "7.3.10" + resolved "https://registry.yarnpkg.com/@mui/styled-engine/-/styled-engine-7.3.10.tgz#53e98c1fdeda972b5932c76f6a2a29faf33f0d11" + integrity sha512-WxE9SiF8xskAQqGjsp0poXCkCqsoXFEsSr0HBXfApmGHR+DBnXRp+z46Vsltg4gpPM4Z96DeAQRpeAOnhNg7Ng== + dependencies: + "@babel/runtime" "^7.28.6" + "@emotion/cache" "^11.14.0" + "@emotion/serialize" "^1.3.3" + "@emotion/sheet" "^1.4.0" + csstype "^3.2.3" + prop-types "^15.8.1" + +"@mui/system@^7.3.11", "@mui/system@^7.3.3": + version "7.3.11" + resolved "https://registry.yarnpkg.com/@mui/system/-/system-7.3.11.tgz#ffb8ba06f43d697db80257b9a2dfc8042b18554a" + integrity sha512-7izwGWdNawAKpBKcRlx7f2gFnAAjmASBWvMcyX4YYEeLOFsbfGRbUYGInvnAcUeql3rPxI7F9Ft4oY2OLRz44g== + dependencies: + "@babel/runtime" "^7.28.6" + "@mui/private-theming" "^7.3.11" + "@mui/styled-engine" "^7.3.10" + "@mui/types" "^7.4.12" + "@mui/utils" "^7.3.11" + clsx "^2.1.1" + csstype "^3.2.3" + prop-types "^15.8.1" + +"@mui/types@^7.4.12": + version "7.4.12" + resolved "https://registry.yarnpkg.com/@mui/types/-/types-7.4.12.tgz#e4eba37a7506419ea5c5e0604322ba82b271bf46" + integrity sha512-iKNAF2u9PzSIj40CjvKJWxFXJo122jXVdrmdh0hMYd+FR+NuJMkr/L88XwWLCRiJ5P1j+uyac25+Kp6YC4hu6w== + dependencies: + "@babel/runtime" "^7.28.6" + +"@mui/utils@^7.3.11": + version "7.3.11" + resolved "https://registry.yarnpkg.com/@mui/utils/-/utils-7.3.11.tgz#493f46f053fe3a692e041b1b6b8295e2f46d9448" + integrity sha512-XTjGnifwteg71/ij+0e7Y7d+hwyntMYP5wPoA/g2drdGH+Flkvjwy0OfrVpKBbaOvofq4zU/LIyUZyKgmWu18g== + dependencies: + "@babel/runtime" "^7.28.6" + "@mui/types" "^7.4.12" + "@types/prop-types" "^15.7.15" + clsx "^2.1.1" + prop-types "^15.8.1" + react-is "^19.2.3" + +"@popperjs/core@^2.11.8": + version "2.11.8" + resolved "https://registry.yarnpkg.com/@popperjs/core/-/core-2.11.8.tgz#6b79032e760a0899cd4204710beede972a3a185f" + integrity sha512-P1st0aksCrn9sGZhp8GMYwBnQsbvAWsZAX44oXNNvLHGqAOcoVxmjZiohstwQ7SqKnbR47akdNi+uleWD8+g6A== + +"@rolldown/pluginutils@1.0.0-rc.3": + version "1.0.0-rc.3" + resolved "https://registry.yarnpkg.com/@rolldown/pluginutils/-/pluginutils-1.0.0-rc.3.tgz#8a88cc92a0f741befc7bc109cb1a4c6b9408e1c5" + integrity sha512-eybk3TjzzzV97Dlj5c+XrBFW57eTNhzod66y9HrBlzJ6NsCrWCp/2kaPS3K9wJmurBC0Tdw4yPjXKZqlznim3Q== + +"@rollup/rollup-android-arm-eabi@4.60.4": + version "4.60.4" + resolved "https://registry.yarnpkg.com/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.60.4.tgz#3a04f01e9f01392bbef5920b94aa3b88794be7ab" + integrity sha512-F5QXMSiFebS9hKZj02XhWLLnRpJ3B3AROP0tWbFBSj+6kCbg5m9j5JoHKd4mmSVy5mS/IMQloYgYxCuJC0fxEQ== + +"@rollup/rollup-android-arm64@4.60.4": + version "4.60.4" + resolved "https://registry.yarnpkg.com/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.60.4.tgz#e371b653ceabc900790ae73f5548a0fd7cd63a70" + integrity sha512-GxxTKApUpzRhof7poWvCJHRF51C67u1R7D6DiluBE8wKU1u5GWE8t+v81JvJYtbawoBFX1hLv5Ei4eVjkWokaw== + +"@rollup/rollup-darwin-arm64@4.60.4": + version "4.60.4" + resolved "https://registry.yarnpkg.com/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.60.4.tgz#2a5aa70432e39816d666d79287a7324cfc3b4e72" + integrity sha512-tua0TaJxMOB1R0V0RS1jFZ/RpURFDJIOR2A6jWwQeawuFyS4gBW+rntLRaQd0EQ4bd6Vp44Z2rXW+YYDBsj6IA== + +"@rollup/rollup-darwin-x64@4.60.4": + version "4.60.4" + resolved "https://registry.yarnpkg.com/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.60.4.tgz#c3b5b49629379cd9cdc5d841bf00ed44ebf393dd" + integrity sha512-CSKq7MsP+5PFIcydhAiR1K0UhEI1A2jWXVKHPCBZ151yOutENwvnPocgVHkivu2kviURtCEB6zUQw0vs8RrhMg== + +"@rollup/rollup-freebsd-arm64@4.60.4": + version "4.60.4" + resolved "https://registry.yarnpkg.com/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.60.4.tgz#f929d8e0462fae6602fc960beeabd7287d859283" + integrity sha512-+O8OkVdyvXMtJEciu2wS/pzm1IxntEEQx3z5TAVy4l32G0etZn+RsA48ARRrFm6Ri8fvqPQfgrvNxSjKAbnd3g== + +"@rollup/rollup-freebsd-x64@4.60.4": + version "4.60.4" + resolved "https://registry.yarnpkg.com/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.60.4.tgz#c01cb58031226f95d0900b1ec847f4fb32c6e809" + integrity sha512-Iw3oMskH3AfNuhU0MSN7vNbdi4me/NiYo2azqPz/Le16zHSa+3RRmliCMWWQmh4lcndccU40xcJuTYJZxNo/lw== + +"@rollup/rollup-linux-arm-gnueabihf@4.60.4": + version "4.60.4" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.60.4.tgz#f29d890c4858c8e0d3be01677eef4f6a359eed9d" + integrity sha512-EIPRXTVQpHyF8WOo219AD2yEltPehLTcTMz2fn6JsatLYSzQf00hj3rulF+yauOlF9/FtM2WpkT/hJh/KJFGhA== + +"@rollup/rollup-linux-arm-musleabihf@4.60.4": + version "4.60.4" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.60.4.tgz#1ebfc8eb9f66136ed2faae5f44995add5ca3c964" + integrity sha512-J3Yh9PzzF1Ovah2At+lHiGQdsYgArxBbXv/zHfSyaiFQEqvNv7DcW98pCrmdjCZBrqBiKrKKe2V+aaSGWuBe/w== + +"@rollup/rollup-linux-arm64-gnu@4.60.4": + version "4.60.4" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.60.4.tgz#c1fa823c2c4ce46ba7f61de1a4c3fdadd4fb4e7b" + integrity sha512-BFDEZMYfUvLn37ONE1yMBojPxnMlTFsdyNoqncT0qFq1mAfllL+ATMMJd8TeuVMiX84s1KbcxcZbXInmcO2mRg== + +"@rollup/rollup-linux-arm64-musl@4.60.4": + version "4.60.4" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.60.4.tgz#a7f18854d0471b78bda8ea38f0891a4e059b571d" + integrity sha512-pc9EYOSlOgdQ2uPl1o9PF6/kLSgaUosia7gOuS8mB69IxJvlclko1MECXysjs5ryez1/5zjYqx3+xYU0TU6R1A== + +"@rollup/rollup-linux-loong64-gnu@4.60.4": + version "4.60.4" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.60.4.tgz#83658a9a4576bcce8cef85b2c78b9b649d2200c4" + integrity sha512-NxnomyxYerDh5n4iLrNa+sH+Z+U4BMEE46V2PgQ/hoB909i8gV1M5wPojWg9fk1jWpO3IQnOs20K4wyZuFLEFQ== + +"@rollup/rollup-linux-loong64-musl@4.60.4": + version "4.60.4" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-loong64-musl/-/rollup-linux-loong64-musl-4.60.4.tgz#fd2af677ae3417bb58d57ae37dd0d84686e40244" + integrity sha512-nbJnQ8a3z1mtmrwImCYhc6BGpThAyYVRQxw9uKSKG4wR6aAYno9sVjJ0zaZcW9BPJX1GbrDPf+SvdWjgTuDmnw== + +"@rollup/rollup-linux-ppc64-gnu@4.60.4": + version "4.60.4" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.60.4.tgz#6481647181c4cf8f1ddbd99f62c84cfc56c1a94a" + integrity sha512-2EU6acNrQLd8tYvo/LXW535wupT3m6fo7HKo6lr7ktQoItxTyOL1ZCR/GfGCuXl2vR+zmfI6eRXkSemafv+iVg== + +"@rollup/rollup-linux-ppc64-musl@4.60.4": + version "4.60.4" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-ppc64-musl/-/rollup-linux-ppc64-musl-4.60.4.tgz#18610a1a1550e28a5042ca916f898419540f17f4" + integrity sha512-WeBtoMuaMxiiIrO2IYP3xs6GMWkJP2C0EoT8beTLkUPmzV1i/UcOSVw1d5r9KBODtHKilG5yFxsGRnBbK3wJ4A== + +"@rollup/rollup-linux-riscv64-gnu@4.60.4": + version "4.60.4" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.60.4.tgz#597bb80465a2621dbe0de0a41c66394a8a7e9a6e" + integrity sha512-FJHFfqpKUI3A10WrWKiFbBZ7yVbGT4q4B5o1qKFFojqpaYoh9LrQgqWCmmcxQzVSXYtyB5bzkXrYzlHTs21MYA== + +"@rollup/rollup-linux-riscv64-musl@4.60.4": + version "4.60.4" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.60.4.tgz#a2a919a9f927ef7f24a60af77e3cb55f1ad59e4d" + integrity sha512-mcEl6CUT5IAUmQf1m9FYSmVqCJlpQ8r8eyftFUHG8i9OhY7BkBXSUdnLH5DOf0wCOjcP9v/QO93zpmF1SptCCw== + +"@rollup/rollup-linux-s390x-gnu@4.60.4": + version "4.60.4" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.60.4.tgz#3166f6ceae7df9bbfddf9f36be1937231e13e3c6" + integrity sha512-ynt3JxVd2w2buzoKDWIyiV1pJW93xlQic1THVLXilz429oijRpSHivZAgp65KBu+cMcgf1eVVjdnTLvPxgCuoQ== + +"@rollup/rollup-linux-x64-gnu@4.60.4": + version "4.60.4" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.60.4.tgz#23c9bf79771d804fb87415eb0767569f273261e5" + integrity sha512-Boiz5+MsaROEWDf+GGEwF8VMHGhlUoQMtIPjOgA5fv4osupqTVnJteQNKJwUcnUog2G55jYXH7KZFFiJe0TEzQ== + +"@rollup/rollup-linux-x64-musl@4.60.4": + version "4.60.4" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.60.4.tgz#97941c6b94d67fe25cde0f027c10a19f2d1fdd39" + integrity sha512-+qfSY27qIrFfI/Hom04KYFw3GKZSGU4lXus51wsb5EuySfFlWRwjkKWoE9emgRw/ukoT4Udsj4W/+xxG8VbPKg== + +"@rollup/rollup-openbsd-x64@4.60.4": + version "4.60.4" + resolved "https://registry.yarnpkg.com/@rollup/rollup-openbsd-x64/-/rollup-openbsd-x64-4.60.4.tgz#7aeb7d92e2cd1d399f56daf75c39040b777b6c77" + integrity sha512-VpTfOPHgVXEBeeR8hZ2O0F3aSso+JDWqTWmTmzcQKted54IAdUVbxE+j/MVxUsKa8L20HJhv3vUezVPoquqWjA== + +"@rollup/rollup-openharmony-arm64@4.60.4": + version "4.60.4" + resolved "https://registry.yarnpkg.com/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.60.4.tgz#925de61ae83bf99aa636e8acea87432e8c0ffaab" + integrity sha512-IPOsh5aRYuLv/nkU51X10Bf75Bsf6+gZdx1X+QP5QM6lIJFHHqbHLG0uJn/hWthzo13UAc2umiUorqZy3axoZg== + +"@rollup/rollup-win32-arm64-msvc@4.60.4": + version "4.60.4" + resolved "https://registry.yarnpkg.com/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.60.4.tgz#888ab83842721491044c46a7407e1f38f3235bb4" + integrity sha512-4QzE9E81OohJ/HKzHhsqU+zcYYojVOXlFMs1DdyMT6qXl/niOH7AVElmmEdUNHHS/oRkc++d5k6Vy85zFs0DEw== + +"@rollup/rollup-win32-ia32-msvc@4.60.4": + version "4.60.4" + resolved "https://registry.yarnpkg.com/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.60.4.tgz#fa30ac24e3f0232139d2a47500560a28695764d4" + integrity sha512-zTPgT1YuHHcd+Tmx7h8aml0FWFVelV5N54oHow9SLj+GfoDy/huQ+UV396N/C7KpMDMiPspRktzM1/0r1usYEA== + +"@rollup/rollup-win32-x64-gnu@4.60.4": + version "4.60.4" + resolved "https://registry.yarnpkg.com/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.60.4.tgz#223e2bc93f86e0707568e1fadb5b537e50c976c7" + integrity sha512-DRS4G7mi9lJxqEDezIkKCaUIKCrLUUDCUaCsTPCi/rtqaC6D/jjwslMQyiDU50Ka0JKpeXeRBFBAXwArY52vBw== + +"@rollup/rollup-win32-x64-msvc@4.60.4": + version "4.60.4" + resolved "https://registry.yarnpkg.com/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.60.4.tgz#da4f1676d87e2bdf744291b504b0ab79550c3e61" + integrity sha512-QVTUovf40zgTqlFVrKA1uXMVvU2QWEFWfAH8Wdc48IxLvrJMQVMBRjuQyUpzZCDkakImib9eVazbWlC6ksWtJw== + +"@tanstack/history@1.162.0": + version "1.162.0" + resolved "https://registry.yarnpkg.com/@tanstack/history/-/history-1.162.0.tgz#5e12a52edd82a5d6d3b081255ec899ccca335443" + integrity sha512-79pf/RkhteYZTRgcR4F9kbk84P2N8rugQJswxfIqovlbRiT3yI7eBE+5QorIrZaOKktsgzRlXh1l/du/xpl4iA== + +"@tanstack/react-router@^1": + version "1.170.9" + resolved "https://registry.yarnpkg.com/@tanstack/react-router/-/react-router-1.170.9.tgz#40db6e8a23daead3f655c9ea5da0b494e5e5fe61" + integrity sha512-rXXztp6GyXFwResQR0jdvkf52wy/sAQqze3OR08+8uNM7nHir1P46qBrwg2TL5EEtX+0WUho4BZ/nMpAgQVB6A== + dependencies: + "@tanstack/history" "1.162.0" + "@tanstack/react-store" "^0.9.3" + "@tanstack/router-core" "1.171.7" + isbot "^5.1.22" + +"@tanstack/react-store@^0.9.3": + version "0.9.3" + resolved "https://registry.yarnpkg.com/@tanstack/react-store/-/react-store-0.9.3.tgz#4d19d982a4612cc21cedf5ab25a62617323509c9" + integrity sha512-y2iHd/N9OkoQbFJLUX1T9vbc2O9tjH0pQRgTcx1/Nz4IlwLvkgpuglXUx+mXt0g5ZDFrEeDnONPqkbfxXJKwRg== + dependencies: + "@tanstack/store" "0.9.3" + use-sync-external-store "^1.6.0" + +"@tanstack/router-core@1.171.7": + version "1.171.7" + resolved "https://registry.yarnpkg.com/@tanstack/router-core/-/router-core-1.171.7.tgz#ff2edaa37e25cd8954d0e92d81534b493c7f3df7" + integrity sha512-AboyQD0OPIu0adJi6HeCupTU9Wx7zr4q4ceJYQdBL3mLH18m4M57XXdzJdtzOg/twl8DtWej0RGJ12ma8CV1iQ== + dependencies: + "@tanstack/history" "1.162.0" + cookie-es "^3.0.0" + seroval "^1.5.4" + seroval-plugins "^1.5.4" + +"@tanstack/store@0.9.3": + version "0.9.3" + resolved "https://registry.yarnpkg.com/@tanstack/store/-/store-0.9.3.tgz#0f9ebca34d194442fe559afce1e8a7327f6d3eb4" + integrity sha512-8reSzl/qGWGGVKhBoxXPMWzATSbZLZFWhwBAFO9NAyp0TxzfBP0mIrGb8CP8KrQTmvzXlR/vFPPUrHTLBGyFyw== + +"@types/babel__core@^7.20.5": + version "7.20.5" + resolved "https://registry.yarnpkg.com/@types/babel__core/-/babel__core-7.20.5.tgz#3df15f27ba85319caa07ba08d0721889bb39c017" + integrity sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA== + dependencies: + "@babel/parser" "^7.20.7" + "@babel/types" "^7.20.7" + "@types/babel__generator" "*" + "@types/babel__template" "*" + "@types/babel__traverse" "*" + +"@types/babel__generator@*": + version "7.27.0" + resolved "https://registry.yarnpkg.com/@types/babel__generator/-/babel__generator-7.27.0.tgz#b5819294c51179957afaec341442f9341e4108a9" + integrity sha512-ufFd2Xi92OAVPYsy+P4n7/U7e68fex0+Ee8gSG9KX7eo084CWiQ4sdxktvdl0bOPupXtVJPY19zk6EwWqUQ8lg== + dependencies: + "@babel/types" "^7.0.0" + +"@types/babel__template@*": + version "7.4.4" + resolved "https://registry.yarnpkg.com/@types/babel__template/-/babel__template-7.4.4.tgz#5672513701c1b2199bc6dad636a9d7491586766f" + integrity sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A== + dependencies: + "@babel/parser" "^7.1.0" + "@babel/types" "^7.0.0" + +"@types/babel__traverse@*": + version "7.28.0" + resolved "https://registry.yarnpkg.com/@types/babel__traverse/-/babel__traverse-7.28.0.tgz#07d713d6cce0d265c9849db0cbe62d3f61f36f74" + integrity sha512-8PvcXf70gTDZBgt9ptxJ8elBeBjcLOAcOtoO/mPJjtji1+CdGbHgm77om1GrsPxsiE+uXIpNSK64UYaIwQXd4Q== + dependencies: + "@babel/types" "^7.28.2" + +"@types/estree@1.0.8": + version "1.0.8" + resolved "https://registry.yarnpkg.com/@types/estree/-/estree-1.0.8.tgz#958b91c991b1867ced318bedea0e215ee050726e" + integrity sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w== + +"@types/node@^24.7.1": + version "24.12.4" + resolved "https://registry.yarnpkg.com/@types/node/-/node-24.12.4.tgz#2709745569811dcbdc57c097fafdd387c6330382" + integrity sha512-GUUEShf+PBCGW2KaXwcIt3Yk+e3pkKwWKb9GSyM9WQVE+ep2jzmHdGsHzu4wgcZy5fN9FBdVzjpBQsYlpfpgLA== + dependencies: + undici-types "~7.16.0" + +"@types/parse-json@^4.0.0": + version "4.0.2" + resolved "https://registry.yarnpkg.com/@types/parse-json/-/parse-json-4.0.2.tgz#5950e50960793055845e956c427fc2b0d70c5239" + integrity sha512-dISoDXWWQwUquiKsyZ4Ng+HX2KsPL7LyHKHQwgGFEA3IaKac4Obd+h2a/a6waisAoepJlBcx9paWqjA8/HVjCw== + +"@types/prop-types@^15.7.15": + version "15.7.15" + resolved "https://registry.yarnpkg.com/@types/prop-types/-/prop-types-15.7.15.tgz#e6e5a86d602beaca71ce5163fadf5f95d70931c7" + integrity sha512-F6bEyamV9jKGAFBEmlQnesRPGOQqS2+Uwi0Em15xenOxHaf2hv6L8YCVn3rPdPJOiJfPiCnLIRyvwVaqMY3MIw== + +"@types/react-dom@^19.2.1": + version "19.2.3" + resolved "https://registry.yarnpkg.com/@types/react-dom/-/react-dom-19.2.3.tgz#c1e305d15a52a3e508d54dca770d202cb63abf2c" + integrity sha512-jp2L/eY6fn+KgVVQAOqYItbF0VY/YApe5Mz2F0aykSO8gx31bYCZyvSeYxCHKvzHG5eZjc+zyaS5BrBWya2+kQ== + +"@types/react-transition-group@^4.4.12": + version "4.4.12" + resolved "https://registry.yarnpkg.com/@types/react-transition-group/-/react-transition-group-4.4.12.tgz#b5d76568485b02a307238270bfe96cb51ee2a044" + integrity sha512-8TV6R3h2j7a91c+1DXdJi3Syo69zzIZbz7Lg5tORM5LEJG7X/E6a1V3drRyBRZq7/utz7A+c4OgYLiLcYGHG6w== + +"@types/react@^19.2.2": + version "19.2.15" + resolved "https://registry.yarnpkg.com/@types/react/-/react-19.2.15.tgz#9e2c6a4251a290f5c525c3143caa872fa6e01e0d" + integrity sha512-eRwcGNHve+E8qtEQSSRl6urh+rFop4v8gm6O8rGv25CodbvFdLjA1vVQ1KkiFE0w0UPOnb8tDiFKL5lp0rtY5Q== + dependencies: + csstype "^3.2.2" + +"@vitejs/plugin-react@^5.0.4": + version "5.2.0" + resolved "https://registry.yarnpkg.com/@vitejs/plugin-react/-/plugin-react-5.2.0.tgz#108bd0f566f288ce3566982df4eff137ded7b15f" + integrity sha512-YmKkfhOAi3wsB1PhJq5Scj3GXMn3WvtQ/JC0xoopuHoXSdmtdStOpFrYaT1kie2YgFBcIe64ROzMYRjCrYOdYw== + dependencies: + "@babel/core" "^7.29.0" + "@babel/plugin-transform-react-jsx-self" "^7.27.1" + "@babel/plugin-transform-react-jsx-source" "^7.27.1" + "@rolldown/pluginutils" "1.0.0-rc.3" + "@types/babel__core" "^7.20.5" + react-refresh "^0.18.0" + +babel-plugin-macros@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/babel-plugin-macros/-/babel-plugin-macros-3.1.0.tgz#9ef6dc74deb934b4db344dc973ee851d148c50c1" + integrity sha512-Cg7TFGpIr01vOQNODXOOaGz2NpCU5gl8x1qJFbb6hbZxR7XrcE2vtbAsTAbJ7/xwJtUuJEw8K8Zr/AE0LHlesg== + dependencies: + "@babel/runtime" "^7.12.5" + cosmiconfig "^7.0.0" + resolve "^1.19.0" + +baseline-browser-mapping@^2.10.12: + version "2.10.32" + resolved "https://registry.yarnpkg.com/baseline-browser-mapping/-/baseline-browser-mapping-2.10.32.tgz#b6b553a4285fdd606327a617de36a5351e3aaa64" + integrity sha512-wbPvpyjJPC0zdfdKXxqEL3Ea+bOMD/87X4lftiJkkaBiuG6ALQy1SLmEd7BSmVCuwCQsBrCamgBoLyfFDD1EPg== + +browserslist@^4.24.0: + version "4.28.2" + resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.28.2.tgz#f50b65362ef48974ca9f50b3680566d786b811d2" + integrity sha512-48xSriZYYg+8qXna9kwqjIVzuQxi+KYWp2+5nCYnYKPTr0LvD89Jqk2Or5ogxz0NUMfIjhh2lIUX/LyX9B4oIg== + dependencies: + baseline-browser-mapping "^2.10.12" + caniuse-lite "^1.0.30001782" + electron-to-chromium "^1.5.328" + node-releases "^2.0.36" + update-browserslist-db "^1.2.3" + +callsites@^3.0.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/callsites/-/callsites-3.1.0.tgz#b3630abd8943432f54b3f0519238e33cd7df2f73" + integrity sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ== + +caniuse-lite@^1.0.30001782: + version "1.0.30001793" + resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001793.tgz#238887ddf5fcfc8c36d872394d0a78a517312a72" + integrity sha512-iwSsYWaCOoh26cV8NwNRViHlrfUvYsHDfRVcbtmw0Kg6PJIZZXwMkj1442FYLBGkeUf1juAsU3DTfxW579mrPA== + +clsx@^2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/clsx/-/clsx-2.1.1.tgz#eed397c9fd8bd882bfb18deab7102049a2f32999" + integrity sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA== + +convert-source-map@^1.5.0: + version "1.9.0" + resolved "https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-1.9.0.tgz#7faae62353fb4213366d0ca98358d22e8368b05f" + integrity sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A== + +convert-source-map@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-2.0.0.tgz#4b560f649fc4e918dd0ab75cf4961e8bc882d82a" + integrity sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg== + +cookie-es@^3.0.0: + version "3.1.1" + resolved "https://registry.yarnpkg.com/cookie-es/-/cookie-es-3.1.1.tgz#c4a8a16cf88cb5a185b23f4a61d6e9a85eb53287" + integrity sha512-UaXxwISYJPTr9hwQxMFYZ7kNhSXboMXP+Z3TRX6f1/NyaGPfuNUZOWP1pUEb75B2HjfklIYLVRfWiFZJyC6Npg== + +cosmiconfig@^7.0.0: + version "7.1.0" + resolved "https://registry.yarnpkg.com/cosmiconfig/-/cosmiconfig-7.1.0.tgz#1443b9afa596b670082ea46cbd8f6a62b84635f6" + integrity sha512-AdmX6xUzdNASswsFtmwSt7Vj8po9IuqXm0UXz7QKPuEUmPB4XyjGfaAr2PSuELMwkRMVH1EpIkX5bTZGRB3eCA== + dependencies: + "@types/parse-json" "^4.0.0" + import-fresh "^3.2.1" + parse-json "^5.0.0" + path-type "^4.0.0" + yaml "^1.10.0" + +cronstrue@^3.3.0: + version "3.14.0" + resolved "https://registry.yarnpkg.com/cronstrue/-/cronstrue-3.14.0.tgz#bc7844ae67959b90742b298aabe7c6d49cdbf931" + integrity sha512-XnW4vuK/jPJjmTyDWiej1Zq36Od7ITwxaV2O1pzHZuyMVvdy7NAvyvIBzybt+idqSpfqYuoDG7uf/ocGtJVWxA== + +csstype@^3.0.2, csstype@^3.2.2, csstype@^3.2.3: + version "3.2.3" + resolved "https://registry.yarnpkg.com/csstype/-/csstype-3.2.3.tgz#ec48c0f3e993e50648c86da559e2610995cf989a" + integrity sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ== + +debug@^4.1.0, debug@^4.3.1: + version "4.4.3" + resolved "https://registry.yarnpkg.com/debug/-/debug-4.4.3.tgz#c6ae432d9bd9662582fce08709b038c58e9e3d6a" + integrity sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA== + dependencies: + ms "^2.1.3" + +dom-helpers@^5.0.1: + version "5.2.1" + resolved "https://registry.yarnpkg.com/dom-helpers/-/dom-helpers-5.2.1.tgz#d9400536b2bf8225ad98fe052e029451ac40e902" + integrity sha512-nRCa7CK3VTrM2NmGkIy4cbK7IZlgBE/PYMn55rrXefr5xXDP0LdtfPnblFDoVdcAfslJ7or6iqAUnx0CCGIWQA== + dependencies: + "@babel/runtime" "^7.8.7" + csstype "^3.0.2" + +electron-to-chromium@^1.5.328: + version "1.5.364" + resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.5.364.tgz#38af950b724b22b937c342c0c4b361bd2fb50669" + integrity sha512-G/dYE3+AYhyHwzTwg8UbnXf7zqMERYh7l2jJ3QujhFsH8agSYwtnGAR2aZ7f0AakIKJXd5En/Hre4igIUrdlYw== + +error-ex@^1.3.1: + version "1.3.4" + resolved "https://registry.yarnpkg.com/error-ex/-/error-ex-1.3.4.tgz#b3a8d8bb6f92eecc1629e3e27d3c8607a8a32414" + integrity sha512-sqQamAnR14VgCr1A618A3sGrygcpK+HEbenA/HiEAkkUwcZIIB/tgWqHFxWgOyDh4nB4JCRimh79dR5Ywc9MDQ== + dependencies: + is-arrayish "^0.2.1" + +es-errors@^1.3.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/es-errors/-/es-errors-1.3.0.tgz#05f75a25dab98e4fb1dcd5e1472c0546d5057c8f" + integrity sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw== + +esbuild@^0.27.0: + version "0.27.7" + resolved "https://registry.yarnpkg.com/esbuild/-/esbuild-0.27.7.tgz#bcadce22b2f3fd76f257e3a64f83a64986fea11f" + integrity sha512-IxpibTjyVnmrIQo5aqNpCgoACA/dTKLTlhMHihVHhdkxKyPO1uBBthumT0rdHmcsk9uMonIWS0m4FljWzILh3w== + optionalDependencies: + "@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" + +escalade@^3.2.0: + version "3.2.0" + resolved "https://registry.yarnpkg.com/escalade/-/escalade-3.2.0.tgz#011a3f69856ba189dffa7dc8fcce99d2a87903e5" + integrity sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA== + +escape-string-regexp@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz#14ba83a5d373e3d311e5afca29cf5bfad965bf34" + integrity sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA== + +fdir@^6.5.0: + version "6.5.0" + resolved "https://registry.yarnpkg.com/fdir/-/fdir-6.5.0.tgz#ed2ab967a331ade62f18d077dae192684d50d350" + integrity sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg== + +find-root@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/find-root/-/find-root-1.1.0.tgz#abcfc8ba76f708c42a97b3d685b7e9450bfb9ce4" + integrity sha512-NKfW6bec6GfKc0SGx1e07QZY9PE99u0Bft/0rzSD5k3sO/vwkVUpDUKVm5Gpp5Ue3YfShPFTX2070tDs5kB9Ng== + +fsevents@~2.3.2, fsevents@~2.3.3: + version "2.3.3" + resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.3.3.tgz#cac6407785d03675a2a5e1a5305c697b347d90d6" + integrity sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw== + +function-bind@^1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.2.tgz#2c02d864d97f3ea6c8830c464cbd11ab6eab7a1c" + integrity sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA== + +gensync@^1.0.0-beta.2: + version "1.0.0-beta.2" + resolved "https://registry.yarnpkg.com/gensync/-/gensync-1.0.0-beta.2.tgz#32a6ee76c3d7f52d46b2b1ae5d93fea8580a25e0" + integrity sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg== + +hasown@^2.0.3: + version "2.0.4" + resolved "https://registry.yarnpkg.com/hasown/-/hasown-2.0.4.tgz#8c62d8cb90beb2aad5d0a5b67581ad9854c3f003" + integrity sha512-T2UbfbBEF32wiepXIsMlTW9+dDYC6wMh/t/vYA4tuOMKqWz/n3vr1NFSxQiyP+zk2mXsoMA/i/7qV6LKut1t1A== + dependencies: + function-bind "^1.1.2" + +hoist-non-react-statics@^3.3.1: + version "3.3.2" + resolved "https://registry.yarnpkg.com/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz#ece0acaf71d62c2969c2ec59feff42a4b1a85b45" + integrity sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw== + dependencies: + react-is "^16.7.0" + +import-fresh@^3.2.1: + version "3.3.1" + resolved "https://registry.yarnpkg.com/import-fresh/-/import-fresh-3.3.1.tgz#9cecb56503c0ada1f2741dbbd6546e4b13b57ccf" + integrity sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ== + dependencies: + parent-module "^1.0.0" + resolve-from "^4.0.0" + +is-arrayish@^0.2.1: + version "0.2.1" + resolved "https://registry.yarnpkg.com/is-arrayish/-/is-arrayish-0.2.1.tgz#77c99840527aa8ecb1a8ba697b80645a7a926a9d" + integrity sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg== + +is-core-module@^2.16.1: + version "2.16.2" + resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.16.2.tgz#3e07450a8080ebce3fbf0cac494f4d2ab324e082" + integrity sha512-evOr8xfXKxE6qSR0hSXL2r3sd7ALj8+7jQEUvPYcm5sgZFdJ+AYzT6yNmJenvIYQBgIGwfwz08sL8zoL7yq2BA== + dependencies: + hasown "^2.0.3" + +isbot@^5.1.22: + version "5.1.40" + resolved "https://registry.yarnpkg.com/isbot/-/isbot-5.1.40.tgz#2878f52e9e4118b4466c268aaf7502dcfd38375b" + integrity sha512-yNeeynhhtIVRBk12tBV4eHNxwB42HzR4Q3Ea7vCOiJhImGaAIdIMrbJtacQlBizGLjUPw+akkFI5Dn9T70XoVQ== + +jotai@^2.15.0: + version "2.20.0" + resolved "https://registry.yarnpkg.com/jotai/-/jotai-2.20.0.tgz#0e3422f6f7141758e8212d72338f9cf3bef5fd45" + integrity sha512-b5GAqgmXmXzB4WPaTH26ppk9Sl7AA9WSQX7yfdM+gJ1rFROiWcVbi97gFuN/yVCojOcbcvop2sfLL+fjxW0JVg== + +"js-tokens@^3.0.0 || ^4.0.0", js-tokens@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499" + integrity sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ== + +jsesc@^3.0.2: + version "3.1.0" + resolved "https://registry.yarnpkg.com/jsesc/-/jsesc-3.1.0.tgz#74d335a234f67ed19907fdadfac7ccf9d409825d" + integrity sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA== + +json-parse-even-better-errors@^2.3.0: + version "2.3.1" + resolved "https://registry.yarnpkg.com/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz#7c47805a94319928e05777405dc12e1f7a4ee02d" + integrity sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w== + +json5@^2.2.3: + version "2.2.3" + resolved "https://registry.yarnpkg.com/json5/-/json5-2.2.3.tgz#78cd6f1a19bdc12b73db5ad0c61efd66c1e29283" + integrity sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg== + +lines-and-columns@^1.1.6: + version "1.2.4" + resolved "https://registry.yarnpkg.com/lines-and-columns/-/lines-and-columns-1.2.4.tgz#eca284f75d2965079309dc0ad9255abb2ebc1632" + integrity sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg== + +loose-envify@^1.4.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/loose-envify/-/loose-envify-1.4.0.tgz#71ee51fa7be4caec1a63839f7e682d8132d30caf" + integrity sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q== + dependencies: + js-tokens "^3.0.0 || ^4.0.0" + +lru-cache@^5.1.1: + version "5.1.1" + resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-5.1.1.tgz#1da27e6710271947695daf6848e847f01d84b920" + integrity sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w== + dependencies: + yallist "^3.0.2" + +ms@^2.1.3: + version "2.1.3" + resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.3.tgz#574c8138ce1d2b5861f0b44579dbadd60c6615b2" + integrity sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA== + +nanoid@^3.3.12: + version "3.3.12" + resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.3.12.tgz#ab3d912e217a6d0a514f00a72a16543a28982c05" + integrity sha512-ZB9RH/39qpq5Vu6Y+NmUaFhQR6pp+M2Xt76XBnEwDaGcVAqhlvxrl3B2bKS5D3NH3QR76v3aSrKaF/Kiy7lEtQ== + +node-releases@^2.0.36: + version "2.0.46" + resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-2.0.46.tgz#d188a129a83f5e03a101aacb58f260f2ee8faaa1" + integrity sha512-GYVXHE2KnrzAfsAjl4uP++evGFCrAU1jta4ubEjIG7YWt/64Gqv66a30yKwWczVjA6j3bM4nBwH7Pk1JmDHaxQ== + +object-assign@^4.1.1: + version "4.1.1" + resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863" + integrity sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg== + +parent-module@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/parent-module/-/parent-module-1.0.1.tgz#691d2709e78c79fae3a156622452d00762caaaa2" + integrity sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g== + dependencies: + callsites "^3.0.0" + +parse-json@^5.0.0: + version "5.2.0" + resolved "https://registry.yarnpkg.com/parse-json/-/parse-json-5.2.0.tgz#c76fc66dee54231c962b22bcc8a72cf2f99753cd" + integrity sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg== + dependencies: + "@babel/code-frame" "^7.0.0" + error-ex "^1.3.1" + json-parse-even-better-errors "^2.3.0" + lines-and-columns "^1.1.6" + +path-parse@^1.0.7: + version "1.0.7" + resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.7.tgz#fbc114b60ca42b30d9daf5858e4bd68bbedb6735" + integrity sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw== + +path-type@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/path-type/-/path-type-4.0.0.tgz#84ed01c0a7ba380afe09d90a8c180dcd9d03043b" + integrity sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw== + +picocolors@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/picocolors/-/picocolors-1.1.1.tgz#3d321af3eab939b083c8f929a1d12cda81c26b6b" + integrity sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA== + +picomatch@^4.0.3, picomatch@^4.0.4: + version "4.0.4" + resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-4.0.4.tgz#fd6f5e00a143086e074dffe4c924b8fb293b0589" + integrity sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A== + +postcss@^8.5.6: + version "8.5.15" + resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.5.15.tgz#d1eaf677a324e9ec02196da2d3fecf4a0b9a735c" + integrity sha512-FfR8sjd4em2T6fb3I2MwAJU7HWVMr9zba+enmQeeWFfCbm+UOC/0X4DS8XtpUTMwWMGbjKYP7xjfNekzyGmB3A== + dependencies: + nanoid "^3.3.12" + picocolors "^1.1.1" + source-map-js "^1.2.1" + +prop-types@^15.6.2, prop-types@^15.8.1: + version "15.8.1" + resolved "https://registry.yarnpkg.com/prop-types/-/prop-types-15.8.1.tgz#67d87bf1a694f48435cf332c24af10214a3140b5" + integrity sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg== + dependencies: + loose-envify "^1.4.0" + object-assign "^4.1.1" + react-is "^16.13.1" + +react-dom@^19.2.0: + version "19.2.6" + resolved "https://registry.yarnpkg.com/react-dom/-/react-dom-19.2.6.tgz#44a81b0bcca22da814c00847d09d01c8615529b7" + integrity sha512-0prMI+hvBbPjsWnxDLxlCGyM8PN6UuWjEUCYmZhO67xIV9Xasa/r/vDnq+Xyq4Lo27g8QSbO5YzARu0D1Sps3g== + dependencies: + scheduler "^0.27.0" + +react-is@^16.13.1, react-is@^16.7.0: + version "16.13.1" + resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.13.1.tgz#789729a4dc36de2999dc156dd6c1d9c18cea56a4" + integrity sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ== + +react-is@^19.2.3: + version "19.2.6" + resolved "https://registry.yarnpkg.com/react-is/-/react-is-19.2.6.tgz#aeee6159b159eb7f520d672cffcc69e7052d288f" + integrity sha512-XjBR15BhXuylgWGuslhDKqlSayuqvqBX91BP8pauG8kd1zY8kotkNWbXksTCNRarse4kuGbe2kIY05ARtwNIvw== + +react-refresh@^0.18.0: + version "0.18.0" + resolved "https://registry.yarnpkg.com/react-refresh/-/react-refresh-0.18.0.tgz#2dce97f4fe932a4d8142fa1630e475c1729c8062" + integrity sha512-QgT5//D3jfjJb6Gsjxv0Slpj23ip+HtOpnNgnb2S5zU3CB26G/IDPGoy4RJB42wzFE46DRsstbW6tKHoKbhAxw== + +react-transition-group@^4.4.5: + version "4.4.5" + resolved "https://registry.yarnpkg.com/react-transition-group/-/react-transition-group-4.4.5.tgz#e53d4e3f3344da8521489fbef8f2581d42becdd1" + integrity sha512-pZcd1MCJoiKiBR2NRxeCRg13uCXbydPnmB4EOeRrY7480qNWO8IIgQG6zlDkm6uRMsURXPuKq0GWtiM59a5Q6g== + dependencies: + "@babel/runtime" "^7.5.5" + dom-helpers "^5.0.1" + loose-envify "^1.4.0" + prop-types "^15.6.2" + +react@^19.2.0: + version "19.2.6" + resolved "https://registry.yarnpkg.com/react/-/react-19.2.6.tgz#3dadb8e12b2a7934c1d5317973e5dce1301f9a4d" + integrity sha512-sfWGGfavi0xr8Pg0sVsyHMAOziVYKgPLNrS7ig+ivMNb3wbCBw3KxtflsGBAwD3gYQlE/AEZsTLgToRrSCjb0Q== + +resolve-from@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-4.0.0.tgz#4abcd852ad32dd7baabfe9b40e00a36db5f392e6" + integrity sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g== + +resolve@^1.19.0: + version "1.22.12" + resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.22.12.tgz#f5b2a680897c69c238a13cd16b15671f8b73549f" + integrity sha512-TyeJ1zif53BPfHootBGwPRYT1RUt6oGWsaQr8UyZW/eAm9bKoijtvruSDEmZHm92CwS9nj7/fWttqPCgzep8CA== + dependencies: + es-errors "^1.3.0" + is-core-module "^2.16.1" + path-parse "^1.0.7" + supports-preserve-symlinks-flag "^1.0.0" + +rollup@^4.43.0: + version "4.60.4" + resolved "https://registry.yarnpkg.com/rollup/-/rollup-4.60.4.tgz#ca3814f5900da3ac3981d2e0c61944b7e6e0cb09" + integrity sha512-WHeFSbZYsPu3+bLoNRUuAO+wavNlocOPf3wSHTP7hcFKVnJeWsYlCDbr3mTS14FCizf9ccIxXA8sGL8zKeQN3g== + dependencies: + "@types/estree" "1.0.8" + optionalDependencies: + "@rollup/rollup-android-arm-eabi" "4.60.4" + "@rollup/rollup-android-arm64" "4.60.4" + "@rollup/rollup-darwin-arm64" "4.60.4" + "@rollup/rollup-darwin-x64" "4.60.4" + "@rollup/rollup-freebsd-arm64" "4.60.4" + "@rollup/rollup-freebsd-x64" "4.60.4" + "@rollup/rollup-linux-arm-gnueabihf" "4.60.4" + "@rollup/rollup-linux-arm-musleabihf" "4.60.4" + "@rollup/rollup-linux-arm64-gnu" "4.60.4" + "@rollup/rollup-linux-arm64-musl" "4.60.4" + "@rollup/rollup-linux-loong64-gnu" "4.60.4" + "@rollup/rollup-linux-loong64-musl" "4.60.4" + "@rollup/rollup-linux-ppc64-gnu" "4.60.4" + "@rollup/rollup-linux-ppc64-musl" "4.60.4" + "@rollup/rollup-linux-riscv64-gnu" "4.60.4" + "@rollup/rollup-linux-riscv64-musl" "4.60.4" + "@rollup/rollup-linux-s390x-gnu" "4.60.4" + "@rollup/rollup-linux-x64-gnu" "4.60.4" + "@rollup/rollup-linux-x64-musl" "4.60.4" + "@rollup/rollup-openbsd-x64" "4.60.4" + "@rollup/rollup-openharmony-arm64" "4.60.4" + "@rollup/rollup-win32-arm64-msvc" "4.60.4" + "@rollup/rollup-win32-ia32-msvc" "4.60.4" + "@rollup/rollup-win32-x64-gnu" "4.60.4" + "@rollup/rollup-win32-x64-msvc" "4.60.4" + fsevents "~2.3.2" + +scheduler@^0.27.0: + version "0.27.0" + resolved "https://registry.yarnpkg.com/scheduler/-/scheduler-0.27.0.tgz#0c4ef82d67d1e5c1e359e8fc76d3a87f045fe5bd" + integrity sha512-eNv+WrVbKu1f3vbYJT/xtiF5syA5HPIMtf9IgY/nKg0sWqzAUEvqY/xm7OcZc/qafLx/iO9FgOmeSAp4v5ti/Q== + +semver@^6.3.1: + version "6.3.1" + resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.1.tgz#556d2ef8689146e46dcea4bfdd095f3434dffcb4" + integrity sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA== + +seroval-plugins@^1.5.4: + version "1.5.4" + resolved "https://registry.yarnpkg.com/seroval-plugins/-/seroval-plugins-1.5.4.tgz#3e7d1910b5a516684046770d201b993c81b1b95a" + integrity sha512-S0xQPhUTefAhNvNWFg0c1J8qJArHt5KdtJ/cFAofo06KD1MVSeFWyl4iiu+ApDIuw0WhjpOfCdgConOfAnLgkw== + +seroval@^1.5.4: + version "1.5.4" + resolved "https://registry.yarnpkg.com/seroval/-/seroval-1.5.4.tgz#9d0cedae244f8213bbbbbcc99c497eb7c945d961" + integrity sha512-46uFvgrXTVxZcUorgSSRZ4y+ieqLLQRMlG4bnCZKW3qI6BZm7Rg4ntMW4p1mILEEBZWrFlcpp0AyIIlM6jD9iw== + +source-map-js@^1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/source-map-js/-/source-map-js-1.2.1.tgz#1ce5650fddd87abc099eda37dcff024c2667ae46" + integrity sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA== + +source-map@^0.5.7: + version "0.5.7" + resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.5.7.tgz#8a039d2d1021d22d1ea14c80d8ea468ba2ef3fcc" + integrity sha512-LbrmJOMUSdEVxIKvdcJzQC+nQhe8FUZQTXQy6+I75skNgn3OoQ0DZA8YnFa7gp8tqtL3KPf1kmo0R5DoApeSGQ== + +stylis@4.2.0: + version "4.2.0" + resolved "https://registry.yarnpkg.com/stylis/-/stylis-4.2.0.tgz#79daee0208964c8fe695a42fcffcac633a211a51" + integrity sha512-Orov6g6BB1sDfYgzWfTHDOxamtX1bE/zo104Dh9e6fqJ3PooipYyfJ0pUmrZO2wAvO8YbEyeFrkV91XTsGMSrw== + +supports-preserve-symlinks-flag@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz#6eda4bd344a3c94aea376d4cc31bc77311039e09" + integrity sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w== + +tinyglobby@^0.2.15: + version "0.2.16" + resolved "https://registry.yarnpkg.com/tinyglobby/-/tinyglobby-0.2.16.tgz#1c3b7eb953fce42b226bc5a1ee06428281aff3d6" + integrity sha512-pn99VhoACYR8nFHhxqix+uvsbXineAasWm5ojXoN8xEwK5Kd3/TrhNn1wByuD52UxWRLy8pu+kRMniEi6Eq9Zg== + dependencies: + fdir "^6.5.0" + picomatch "^4.0.4" + +typescript@^5.9.3: + version "5.9.3" + resolved "https://registry.yarnpkg.com/typescript/-/typescript-5.9.3.tgz#5b4f59e15310ab17a216f5d6cf53ee476ede670f" + integrity sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw== + +undici-types@~7.16.0: + version "7.16.0" + resolved "https://registry.yarnpkg.com/undici-types/-/undici-types-7.16.0.tgz#ffccdff36aea4884cbfce9a750a0580224f58a46" + integrity sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw== + +update-browserslist-db@^1.2.3: + version "1.2.3" + resolved "https://registry.yarnpkg.com/update-browserslist-db/-/update-browserslist-db-1.2.3.tgz#64d76db58713136acbeb4c49114366cc6cc2e80d" + integrity sha512-Js0m9cx+qOgDxo0eMiFGEueWztz+d4+M3rGlmKPT+T4IS/jP4ylw3Nwpu6cpTTP8R1MAC1kF4VbdLt3ARf209w== + dependencies: + escalade "^3.2.0" + picocolors "^1.1.1" + +use-sync-external-store@^1.6.0: + version "1.6.0" + resolved "https://registry.yarnpkg.com/use-sync-external-store/-/use-sync-external-store-1.6.0.tgz#b174bfa65cb2b526732d9f2ac0a408027876f32d" + integrity sha512-Pp6GSwGP/NrPIrxVFAIkOQeyw8lFenOHijQWkUTrDvrF4ALqylP2C/KCkeS9dpUM3KvYRQhna5vt7IL95+ZQ9w== + +vite@^7.1.9: + version "7.3.3" + resolved "https://registry.yarnpkg.com/vite/-/vite-7.3.3.tgz#d7e07a52b5873fb86f902a3f4b3d17410337450f" + integrity sha512-/4XH147Ui7OGTjg3HbdWe5arnZQSbfuRzdr9Ec7TQi5I7R+ir0Rlc9GIvD4v0XZurELqA035KVXJXpR61xhiTA== + dependencies: + esbuild "^0.27.0" + fdir "^6.5.0" + picomatch "^4.0.3" + postcss "^8.5.6" + rollup "^4.43.0" + tinyglobby "^0.2.15" + optionalDependencies: + fsevents "~2.3.3" + +yallist@^3.0.2: + version "3.1.1" + resolved "https://registry.yarnpkg.com/yallist/-/yallist-3.1.1.tgz#dbb7daf9bfd8bac9ab45ebf602b8cbad0d5d08fd" + integrity sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g== + +yaml@^1.10.0: + version "1.10.3" + resolved "https://registry.yarnpkg.com/yaml/-/yaml-1.10.3.tgz#76e407ed95c42684fb8e14641e5de62fe65bbcb3" + integrity sha512-vIYeF1u3CjlhAFekPPAk2h/Kv4T3mAkMox5OymRiJQB0spDP10LHvt+K7G9Ny6NuuMAb25/6n1qyUjAcGNf/AA== diff --git a/package.json b/package.json index bcf903a..3bb3b74 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "material-ui-cron", - "version": "1.1.1", + "version": "1.1.2", "description": "A React cron editor using material ui", "author": "Parichay Barpanda (https://github.com/baymac/)", "license": "MIT", @@ -29,30 +29,39 @@ "types": "dist/index.d.ts", "scripts": { "build": "vite build", + "test": "vitest run", + "test:unit": "vitest run --project unit", + "test:browser": "vitest run --project browser", + "test:watch": "vitest", + "coverage": "vitest run --coverage", "lint": "biome lint --write .", "format": "biome format --write .", - "dev": "storybook dev -p 6006", - "storybook": "storybook dev -p 6006", - "build-storybook": "storybook build" + "demo": "yarn --cwd demo dev", + "demo:install": "yarn --cwd demo install", + "demo:build": "yarn --cwd demo build" }, "devDependencies": { "@biomejs/biome": "^2.2.5", "@emotion/react": "^11.14.0", "@emotion/styled": "^11.14.1", - "@storybook/addon-links": "^9.1.10", - "@storybook/react": "^9.1.10", - "@storybook/react-vite": "^9.1.10", - "@storybook/test": "^8.6.14", + "@testing-library/dom": "^10", + "@testing-library/jest-dom": "^6", + "@testing-library/react": "^16", + "@testing-library/user-event": "^14", "@types/node": "^24.7.1", "@types/react": "^19.2.2", "@types/react-dom": "^19.2.1", "@vitejs/plugin-react": "^5.0.4", + "@vitest/browser": "^3", + "@vitest/coverage-v8": "^3", + "playwright": "^1", "react": "^19.2.0", "react-dom": "^19.2.0", - "storybook": "^9.1.10", "typescript": "^5.9.3", "vite": "^7.1.9", - "vite-plugin-dts": "^4.5.4" + "vite-plugin-dts": "^4.5.4", + "vitest": "^3", + "vitest-browser-react": "^1" }, "peerDependencies": { "@emotion/react": "^11.11.0", @@ -67,5 +76,8 @@ "cronstrue": "^3.3.0", "jotai": "^2.15.0" }, - "packageManager": "yarn@1.22.22+sha512.a6b2f7906b721bba3d67d4aff083df04dad64c399707841b7acf00f6b133b7ac24255f2652fa22ae3534329dc6180534e98d17432037ff6fd140556e2bb3137e" + "packageManager": "yarn@1.22.22+sha512.a6b2f7906b721bba3d67d4aff083df04dad64c399707841b7acf00f6b133b7ac24255f2652fa22ae3534329dc6180534e98d17432037ff6fd140556e2bb3137e", + "resolutions": { + "vite": "7.1.9" + } } diff --git a/src/Scheduler.tsx b/src/Scheduler.tsx index cfa0f44..41d7c6c 100644 --- a/src/Scheduler.tsx +++ b/src/Scheduler.tsx @@ -14,10 +14,13 @@ import { supportedLanguages } from './i18n'; import { cronExpInputState, cronValidationErrorMessageState, + dayOfMonthAtEveryState, dayOfMonthState, + hourAtEveryState, hourState, isAdminState, localeState, + minuteAtEveryState, minuteState, monthState, periodState, @@ -26,13 +29,16 @@ import { import type { SchedulerProps } from './types'; import { getPeriodIndex } from './utils'; import { + atEveryOptions, DEFAULT_DAY_OF_MONTH_OPTS, DEFAULT_HOUR_OPTS_EVERY, DEFAULT_MINUTE_OPTS, getPeriodOptions, getMonthOptions, + onEveryOptions, weekOptions, } from './constants'; +import type { SelectOptions } from './types'; const StyledBox = styled(Box)({ minHeight: 'min-content', @@ -63,6 +69,9 @@ export default function Scheduler(props: SchedulerProps) { const setWeek = useSetAtom(weekState); const setMonth = useSetAtom(monthState); const setPeriod = useSetAtom(periodState); + const setMinuteAtEvery = useSetAtom(minuteAtEveryState); + const setHourAtEvery = useSetAtom(hourAtEveryState); + const setDayOfMonthAtEvery = useSetAtom(dayOfMonthAtEveryState); React.useEffect(() => { setCronError(cronError); @@ -72,9 +81,37 @@ export default function Scheduler(props: SchedulerProps) { setPeriodIndex(getPeriodIndex(period)); }, [period]); + // Two-way binding between the controlled `cron` prop and the internal + // `cronExpInput` atom. Splitting this into two opposing effects causes them + // to swap stale values on a single commit and ping-pong forever whenever the + // initial prop differs from the atom default (-> "Maximum update depth + // exceeded"). A single effect that propagates only the side that actually + // changed converges in one render. + const prevSync = React.useRef<{ cron: string; input: string } | null>(null); React.useEffect(() => { - setCron(cronExpInput); - }, [cronExpInput, setCron]); + if (prevSync.current === null) { + // Initial mount: adopt a non-default prop, otherwise report the current + // expression back up so the parent always receives the (normalized) + // value at least once. + if (cron !== cronExpInput) { + setCronExpInput(cron); + } else { + setCron(cronExpInput); + } + prevSync.current = { cron, input: cronExpInput }; + return; + } + const cronChanged = cron !== prevSync.current.cron; + const inputChanged = cronExpInput !== prevSync.current.input; + if (cronChanged && cron !== cronExpInput) { + // Parent pushed a new value down -> ingest it. + setCronExpInput(cron); + } else if (inputChanged && cronExpInput !== cron) { + // Internal value changed (user edit / field change) -> notify parent. + setCron(cronExpInput); + } + prevSync.current = { cron, input: cronExpInput }; + }, [cron, cronExpInput, setCron, setCronExpInput]); React.useEffect(() => { if (isAdmin) { @@ -84,31 +121,34 @@ export default function Scheduler(props: SchedulerProps) { } }, [isAdmin, setIsAdmin]); + // Only reset atoms on unmount. The atoms are module-level (shared) globals, + // so we restore their defaults when the component leaves the tree. Keep + // `currentLocale` out of the dependency array: with it in deps, React fires + // this effect's cleanup on every locale change (and re-mount via `key`), + // resetting the shared atoms to the *previously captured* locale's defaults. + // That is exactly the "period stays one language behind" bug — switch to + // Chinese and the period label resets to English; switch back and it shows + // Chinese. Read the latest locale through a ref so the cleanup runs only on + // a real unmount and uses the current locale. + const localeRef = React.useRef(currentLocale); React.useEffect(() => { - setCronExpInput(cron); - }, [cron, setCronExpInput]); - - // Only reset atoms on unmount. + localeRef.current = currentLocale; + }, [currentLocale]); React.useEffect(() => { return () => { + const loc = localeRef.current; setCronExpInput('0 0 * * *'); setMinute([DEFAULT_MINUTE_OPTS[0]]); setHour([DEFAULT_HOUR_OPTS_EVERY[0]]); setDayOfMonth(DEFAULT_DAY_OF_MONTH_OPTS); - setWeek(weekOptions(currentLocale.weekDaysOptions)); - setMonth(getMonthOptions(currentLocale.shortMonthOptions)); - setPeriod(getPeriodOptions(currentLocale.periodOptions)[1]); + setWeek(weekOptions(loc.weekDaysOptions)); + setMonth(getMonthOptions(loc.shortMonthOptions)); + setPeriod(getPeriodOptions(loc.periodOptions)[1]); }; - }, [ - setCronExpInput, - setMinute, - setHour, - setDayOfMonth, - setWeek, - setMonth, - setPeriod, - currentLocale, - ]); + // Setters from `useSetAtom` are stable; the locale is read via ref. Empty + // deps make this a true unmount-only cleanup. + // eslint-disable-next-line react-hooks/exhaustive-deps + }, []); React.useEffect(() => { if (customLocale) { @@ -120,6 +160,39 @@ export default function Scheduler(props: SchedulerProps) { } }, [locale, customLocale, setResolvedLocale]); + // Re-localize the *selected* values when the locale changes. The option + // `value`s are locale-stable ('day', 'at', '0'...) while only the `label` is + // translated, so a stored selection keeps a stale label after a locale + // switch (e.g. period reads "day" under Chinese). Re-map each selection onto + // the matching option in the new locale, preserving the choice and + // refreshing its label. Falls back to the existing value if no match. + React.useEffect(() => { + const remap = (opts: SelectOptions[]) => (prev: SelectOptions) => + opts.find((o) => o.value === prev.value) ?? prev; + const remapList = (opts: SelectOptions[]) => (prev: SelectOptions[]) => + prev.map((item) => opts.find((o) => o.value === item.value) ?? item); + + setPeriod(remap(getPeriodOptions(currentLocale.periodOptions))); + + const atEvery = atEveryOptions(currentLocale.atOptionLabel, currentLocale.everyOptionLabel); + setMinuteAtEvery(remap(atEvery)); + setHourAtEvery(remap(atEvery)); + setDayOfMonthAtEvery( + remap(onEveryOptions(currentLocale.onOptionLabel, currentLocale.everyOptionLabel)), + ); + + setWeek(remapList(weekOptions(currentLocale.weekDaysOptions))); + setMonth(remapList(getMonthOptions(currentLocale.shortMonthOptions))); + }, [ + currentLocale, + setPeriod, + setMinuteAtEvery, + setHourAtEvery, + setDayOfMonthAtEvery, + setWeek, + setMonth, + ]); + return ( <> diff --git a/src/components/CronExp.tsx b/src/components/CronExp.tsx index fc1c19a..1b377a3 100644 --- a/src/components/CronExp.tsx +++ b/src/components/CronExp.tsx @@ -78,9 +78,9 @@ export default function CronExp() { React.useEffect(() => { if (debouncedCronExpInput) { - setCronExp(cronExpInput); + setCronExp(debouncedCronExpInput); } - }, [debouncedCronExpInput, setCronExp, cronExpInput]); + }, [debouncedCronExpInput, setCronExp]); return ( diff --git a/src/components/CustomSelect.tsx b/src/components/CustomSelect.tsx index e0537f8..4e714f1 100644 --- a/src/components/CustomSelect.tsx +++ b/src/components/CustomSelect.tsx @@ -82,6 +82,11 @@ export default function CustomSelect(props: CustomSelectProps) { disableClearable={disableClearable} autoComplete disableCloseOnSelect={!single} + // Single-value selects must close on selection. Because these are + // rendered as `multiple` Autocompletes, re-render churn after a + // selection can otherwise leave the popup open — blurOnSelect forces + // it shut. Multi-selects keep the popup open for further picks. + blurOnSelect={single ? true : undefined} sx={{ width: sizeConfig.width, '& .MuiAutocomplete-inputRoot': { diff --git a/src/jest-dom.d.ts b/src/jest-dom.d.ts new file mode 100644 index 0000000..c77bb8b --- /dev/null +++ b/src/jest-dom.d.ts @@ -0,0 +1,4 @@ +// Makes the @testing-library/jest-dom matchers (toBeInTheDocument, toBeDisabled, +// ...) available on Vitest's `expect` for type-checking the test files. The +// matching runtime augmentation is loaded via vitest.setup.browser.ts. +import '@testing-library/jest-dom/vitest'; diff --git a/src/scheduler.browser.test.tsx b/src/scheduler.browser.test.tsx new file mode 100644 index 0000000..888f39a --- /dev/null +++ b/src/scheduler.browser.test.tsx @@ -0,0 +1,195 @@ +import { cleanup, fireEvent, render, screen, waitFor, within } from '@testing-library/react'; +import userEvent from '@testing-library/user-event'; +import { afterEach, describe, expect, it, vi } from 'vitest'; +import Scheduler from './index'; + +const noop = () => {}; + +afterEach(() => { + // Unmounting runs Scheduler's reset effect, restoring the shared Jotai atoms. + cleanup(); +}); + +const periodValue = () => (screen.getByLabelText('Period') as HTMLInputElement).value; + +describe('Scheduler (browser)', () => { + it('renders the default expression and core fields', async () => { + render(); + + expect(await screen.findByDisplayValue('0 0 * * *')).toBeInTheDocument(); + await waitFor(() => expect(periodValue()).toBe('day')); + expect(screen.getByLabelText('Minute(s)')).toBeInTheDocument(); + expect(screen.getByLabelText('Hour(s)')).toBeInTheDocument(); + }); + + it('disables the cron input for non-admins', async () => { + render(); + expect(await screen.findByDisplayValue('0 0 * * *')).toBeDisabled(); + }); + + it('reports the cron expression back through setCron', async () => { + const setCron = vi.fn(); + render(); + await waitFor(() => expect(setCron).toHaveBeenCalledWith('0 0 * * *')); + }); + + // ---- #19: `0 1/4 * * *` must be accepted ---- + it('accepts a plain n/m step expression without error (#19)', async () => { + const setCronError = vi.fn(); + render(); + + const input = await screen.findByDisplayValue('0 0 * * *'); + fireEvent.change(input, { target: { value: '0 1/4 * * *' } }); + + // The value is parsed and normalised (1/4 -> the equivalent 1-23/4). What + // matters for #19 is that it validates cleanly: no error surfaces. + await waitFor(() => expect(setCronError).toHaveBeenLastCalledWith(''), { timeout: 3000 }); + expect(screen.queryByText(/Invalid .* cron part/i)).not.toBeInTheDocument(); + }); + + it('still flags a genuinely invalid expression', async () => { + const setCronError = vi.fn(); + render(); + + const input = await screen.findByDisplayValue('0 0 * * *'); + fireEvent.change(input, { target: { value: '60 * * * *' } }); + + await waitFor( + () => expect(setCronError).toHaveBeenLastCalledWith(expect.stringMatching(/Invalid minute/i)), + { timeout: 3000 }, + ); + }); + + // ---- #20: changing the prop to a broader cron resets the period ---- + it('resets the period when the cron prop broadens (#20)', async () => { + const { rerender } = render( + , + ); + await waitFor(() => expect(periodValue()).toBe('week'), { timeout: 3000 }); + + rerender(); + await waitFor(() => expect(screen.getByDisplayValue('* * * * *')).toBeInTheDocument(), { + timeout: 3000, + }); + await waitFor(() => expect(periodValue()).toBe('hour'), { timeout: 3000 }); + }); + + it('still widens the period when the prop narrows', async () => { + const { rerender } = render( + , + ); + await waitFor(() => expect(periodValue()).toBe('day'), { timeout: 3000 }); + + rerender(); + await waitFor(() => expect(periodValue()).toBe('month'), { timeout: 3000 }); + }); + + it('renders month and day-of-month fields for a yearly expression', async () => { + render(); + await waitFor(() => expect(periodValue()).toBe('year'), { timeout: 3000 }); + expect(screen.getByLabelText('Month(s)')).toBeInTheDocument(); + expect(screen.getByLabelText('Day of the Month')).toBeInTheDocument(); + expect(screen.getByLabelText('Hour(s)')).toBeInTheDocument(); + }); + + it('renders a day-of-month range expression', async () => { + render(); + await waitFor(() => expect(periodValue()).toBe('month'), { timeout: 3000 }); + expect(await screen.findByDisplayValue('0 0 1-15 * *')).toBeInTheDocument(); + }); + + it('renders a weekday range expression', async () => { + render(); + await waitFor(() => expect(periodValue()).toBe('week'), { timeout: 3000 }); + expect(screen.getByLabelText('Week Days')).toBeInTheDocument(); + }); + + // ---- #16: single-value autocompletes close after a selection ---- + it('closes a single-value autocomplete after selecting an option (#16)', async () => { + const user = userEvent.setup(); + render(); + + const periodInput = await screen.findByLabelText('Period'); + await user.click(periodInput); + + const listbox = await screen.findByRole('listbox'); + const weekOption = within(listbox).getByText('week'); + await user.click(weekOption); + + // The popup must close on selection (blurOnSelect for single selects). + await waitFor(() => expect(screen.queryByRole('listbox')).not.toBeInTheDocument()); + expect(periodValue()).toBe('week'); + }); + + // #16 deeper audit: the issue specifically named the *time-range* selects. + // Unlike Period, these recompute their option arrays on every selection + // (Hour.tsx / Minute.tsx effects) — the re-render churn that used to keep the + // popup mounted. This guards that blurOnSelect closes them despite that churn. + it('closes a time-range autocomplete after selecting an option (#16)', async () => { + const user = userEvent.setup(); + render(); + + // `0 */4 * * *` puts the hour field in "every" mode, revealing the + // between/and time-range selects (start defaults to 12:00 AM, end 11:00 PM). + const startInput = await screen.findByDisplayValue('12:00 AM'); + await user.click(startInput); + + const listbox = await screen.findByRole('listbox'); + await user.click(within(listbox).getByText('02:00 AM')); + + await waitFor(() => expect(screen.queryByRole('listbox')).not.toBeInTheDocument()); + expect(screen.getByDisplayValue('02:00 AM')).toBeInTheDocument(); + }); + + // #18: the linked upstream bug (mui/material-ui#27501) was a MUI 5 *beta* + // regression. On MUI v7 it does not reproduce; these assertions lock in the + // two styling behaviours the library actually maintains so a future MUI bump + // can't silently regress them. + it('keeps disabled cron-input text legible on the dark field (#18)', async () => { + render(); + const input = (await screen.findByDisplayValue('0 0 * * *')) as HTMLInputElement; + expect(input).toBeDisabled(); + // The component forces white text-fill on the disabled (read-only) input. + expect(getComputedStyle(input).webkitTextFillColor).toBe('rgb(255, 255, 255)'); + }); + + it('renders multi-select values as chips (#18)', async () => { + render(); + await waitFor(() => expect(periodValue()).toBe('week'), { timeout: 3000 }); + // Mon–Fri render as MUI chips inside the Week Days field. + expect(document.querySelectorAll('.MuiChip-root').length).toBeGreaterThan(0); + }); + + // #17 kitchen-sink coverage: a provided locale localizes the field labels. + it('renders localized labels for a provided locale (zh_CN)', async () => { + render(); + // Period label in zh_CN. + await waitFor(() => expect(screen.getByLabelText('时间间隔')).toBeInTheDocument(), { + timeout: 3000, + }); + }); + + // Regression: switching locale left the *selected value* one language behind + // (period read "day" under Chinese; switching back read "天" under English) + // because the shared atoms kept their stale localized labels. The fix + // re-localizes selections onto the new locale's matching option by value. + // Found by /qa on 2026-05-29. + it('re-localizes the selected period when the locale changes (no stale lag)', async () => { + const periodValueZh = () => + (screen.getByLabelText('时间间隔') as HTMLInputElement).value; + + const { rerender } = render( + , + ); + // Month-level cron -> period reads "month" in English. + await waitFor(() => expect(periodValue()).toBe('month'), { timeout: 3000 }); + + // Switch to Chinese: the selection is preserved and its label translates. + rerender(); + await waitFor(() => expect(periodValueZh()).toBe('月'), { timeout: 3000 }); + + // Switch back to English: no Chinese lag — it reads "month" again. + rerender(); + await waitFor(() => expect(periodValue()).toBe('month'), { timeout: 3000 }); + }); +}); diff --git a/src/selector.test.ts b/src/selector.test.ts new file mode 100644 index 0000000..ad30fa8 --- /dev/null +++ b/src/selector.test.ts @@ -0,0 +1,232 @@ +import { createStore } from 'jotai'; +import { beforeEach, describe, expect, it } from 'vitest'; +import { + DEFAULT_DAY_OF_MONTH_OPTS, + DEFAULT_HOUR_OPTS_EVERY, + DEFAULT_MINUTE_OPTS, + getPeriodOptions, +} from './constants'; +import { + cronExpState, + dayOfMonthCronState, + dayOfWeekCronState, + hourCronState, + minuteCronState, + monthCronState, +} from './selector'; +import { + dayOfMonthState, + hourState, + minuteAtEveryState, + minuteRangeEndSchedulerState, + minuteRangeStartSchedulerState, + minuteState, + monthState, + periodState, + weekState, +} from './store'; +import type { SelectOptions } from './types'; + +type Store = ReturnType; +let store: Store; + +const en = ['hour', 'day', 'week', 'month', 'year']; +const period = (value: string): SelectOptions => getPeriodOptions(en)[en.indexOf(value)]; + +beforeEach(() => { + store = createStore(); +}); + +describe('minuteCronState', () => { + it('is `*` when every minute is selected', () => { + store.set(minuteState, DEFAULT_MINUTE_OPTS); + expect(store.get(minuteCronState)).toBe('*'); + }); + + it('emits a single minute', () => { + store.set(minuteState, [DEFAULT_MINUTE_OPTS[5]]); + expect(store.get(minuteCronState)).toBe('5'); + }); + + it('emits a comma list for a non-contiguous selection', () => { + store.set(minuteState, [DEFAULT_MINUTE_OPTS[1], DEFAULT_MINUTE_OPTS[5]]); + expect(store.get(minuteCronState)).toBe('1,5'); + }); + + it('emits a hyphen range for a contiguous selection', () => { + store.set(minuteState, [ + DEFAULT_MINUTE_OPTS[1], + DEFAULT_MINUTE_OPTS[2], + DEFAULT_MINUTE_OPTS[3], + ]); + expect(store.get(minuteCronState)).toBe('1-3'); + }); +}); + +describe('minuteCronState (every / step)', () => { + const every: SelectOptions = { value: 'every', label: 'every' }; + + it('emits `*/n` when the range spans the whole hour', () => { + store.set(minuteAtEveryState, every); + store.set(minuteRangeStartSchedulerState, { value: '0', label: '0' }); + store.set(minuteRangeEndSchedulerState, { value: '59', label: '59' }); + store.set(minuteState, [DEFAULT_MINUTE_OPTS[5]]); + expect(store.get(minuteCronState)).toBe('*/5'); + }); + + it('emits `start-end/n` for a partial range', () => { + store.set(minuteAtEveryState, every); + store.set(minuteRangeStartSchedulerState, { value: '0', label: '0' }); + store.set(minuteRangeEndSchedulerState, { value: '10', label: '10' }); + store.set(minuteState, [DEFAULT_MINUTE_OPTS[5]]); + expect(store.get(minuteCronState)).toBe('0-10/5'); + }); +}); + +describe('dayOfMonthCronState (ranges, lists, last day)', () => { + it('emits a hyphen range', () => { + store.set(periodState, period('month')); + store.set( + dayOfMonthState, + [1, 2, 3].map((d) => ({ value: String(d), label: String(d) })), + ); + expect(store.get(dayOfMonthCronState)).toBe('1-3'); + }); + + it('emits a comma list', () => { + store.set(periodState, period('month')); + store.set( + dayOfMonthState, + [1, 5, 9].map((d) => ({ value: String(d), label: String(d) })), + ); + expect(store.get(dayOfMonthCronState)).toBe('1,5,9'); + }); + + it('emits L for the last day of month', () => { + store.set(periodState, period('month')); + store.set(dayOfMonthState, [{ value: 'L', label: 'Last day of month' }]); + expect(store.get(dayOfMonthCronState)).toBe('L'); + }); +}); + +describe('monthCronState (year period)', () => { + it('emits a hyphen range', () => { + store.set(periodState, period('year')); + store.set( + monthState, + [1, 2, 3].map((m) => ({ value: String(m), label: String(m) })), + ); + expect(store.get(monthCronState)).toBe('1-3'); + }); + + it('emits a comma list', () => { + store.set(periodState, period('year')); + store.set( + monthState, + [1, 6, 12].map((m) => ({ value: String(m), label: String(m) })), + ); + expect(store.get(monthCronState)).toBe('1,6,12'); + }); +}); + +describe('dayOfWeekCronState (ranges and lists)', () => { + it('emits a hyphen range', () => { + store.set(periodState, period('week')); + store.set( + weekState, + [1, 2, 3, 4, 5].map((d) => ({ value: String(d), label: String(d) })), + ); + expect(store.get(dayOfWeekCronState)).toBe('1-5'); + }); + + it('emits a comma list', () => { + store.set(periodState, period('week')); + store.set( + weekState, + [1, 3, 5].map((d) => ({ value: String(d), label: String(d) })), + ); + expect(store.get(dayOfWeekCronState)).toBe('1,3,5'); + }); +}); + +describe('hourCronState', () => { + it('is `*` when the period is narrower than a day', () => { + store.set(periodState, period('hour')); + expect(store.get(hourCronState)).toBe('*'); + }); + + it('emits a single hour at day period', () => { + store.set(periodState, period('day')); + store.set(hourState, [DEFAULT_HOUR_OPTS_EVERY[9]]); + expect(store.get(hourCronState)).toBe('9'); + }); +}); + +describe('dayOfMonthCronState', () => { + it('is `*` until the period reaches month', () => { + store.set(periodState, period('week')); + expect(store.get(dayOfMonthCronState)).toBe('*'); + }); + + it('emits a single day at month period', () => { + store.set(periodState, period('month')); + store.set(dayOfMonthState, [DEFAULT_DAY_OF_MONTH_OPTS[4]]); // 5th + expect(store.get(dayOfMonthCronState)).toBe('5'); + }); +}); + +describe('monthCronState', () => { + it('is `*` below the year period', () => { + store.set(periodState, period('month')); + expect(store.get(monthCronState)).toBe('*'); + }); +}); + +describe('dayOfWeekCronState', () => { + it('is `*` below the week period', () => { + store.set(periodState, period('day')); + expect(store.get(dayOfWeekCronState)).toBe('*'); + }); + + it('emits a single weekday at week period', () => { + store.set(periodState, period('week')); + store.set(weekState, [{ value: '1', label: 'Monday' }]); + expect(store.get(dayOfWeekCronState)).toBe('1'); + }); +}); + +describe('cronExpState (read getter composition)', () => { + it('joins the five derived parts', () => { + expect(store.get(cronExpState)).toBe('0 0 * * *'); + }); + + it('reflects field + period state', () => { + store.set(periodState, period('week')); + store.set(minuteState, [DEFAULT_MINUTE_OPTS[30]]); + store.set(hourState, [DEFAULT_HOUR_OPTS_EVERY[9]]); + store.set(weekState, [{ value: '1', label: 'Monday' }]); + expect(store.get(cronExpState)).toBe('30 9 * * 1'); + }); +}); + +describe('cronExpState writer (parsing into field atoms)', () => { + // The period atom is `atomWithDefault`; its override does not persist in an + // unmounted Node store (it persists once mounted, as in the browser). So these + // assertions check the plain field atoms that the writer parses into. + it('parses minutes/hours into the field atoms', () => { + store.set(cronExpState, '30 9 * * *'); + expect(store.get(minuteState).map((m) => m.value)).toEqual(['30']); + expect(store.get(hourState).map((h) => h.value)).toEqual(['9']); + }); + + it('parses a day-of-month value', () => { + store.set(cronExpState, '0 0 5 * *'); + expect(store.get(dayOfMonthState).map((d) => d.value)).toEqual(['5']); + }); + + it('does not mutate field atoms for an invalid expression', () => { + store.set(minuteState, [DEFAULT_MINUTE_OPTS[7]]); + store.set(cronExpState, '99 99 * * *'); // invalid + expect(store.get(minuteState).map((m) => m.value)).toEqual(['7']); + }); +}); diff --git a/src/selector.ts b/src/selector.ts index a1b746f..dadceeb 100644 --- a/src/selector.ts +++ b/src/selector.ts @@ -68,7 +68,7 @@ export const hourCronState = atom((get) => { if ( getPeriodIndex(get(periodState)) < 1 || hours.map((hour) => hour.value).join('') === - DEFAULT_HOUR_OPTS_EVERY.map((hour) => hour.value).join('') + DEFAULT_HOUR_OPTS_EVERY.map((hour) => hour.value).join('') ) { return '*'; } else if (get(hourAtEveryState).value === 'every') { @@ -90,7 +90,7 @@ export const dayOfMonthCronState = atom((get) => { if ( getPeriodIndex(get(periodState)) < 3 || dayOfMonth.map((dayOfMonth) => dayOfMonth.value).join('') === - DEFAULT_DAY_OF_MONTH_OPTS.map((day) => day.value).join('') + DEFAULT_DAY_OF_MONTH_OPTS.map((day) => day.value).join('') ) { return '*'; } else if (get(dayOfMonthAtEveryState).value === 'every') { @@ -99,9 +99,7 @@ export const dayOfMonthCronState = atom((get) => { if (Number(endIndex) - Number(startIndex) === 30) { return `*/${dayOfMonth.map((dayOfMonth) => dayOfMonth.value).join('')}`; } - return `${startIndex}-${endIndex}/${dayOfMonth - .map((dayOfMonth) => dayOfMonth.value) - .join('')}`; + return `${startIndex}-${endIndex}/${dayOfMonth.map((dayOfMonth) => dayOfMonth.value).join('')}`; } else if (dayOfMonth[0].value === 'L') { return 'L'; } else if (isIncreasingSequence(dayOfMonth) && dayOfMonth.length !== 1) { @@ -118,9 +116,9 @@ export const monthCronState = atom((get) => { get(monthState) .map((month) => month.value) .join('') === - getMonthOptions(get(localeState).shortMonthOptions) - .map((month) => month.value) - .join('') + getMonthOptions(get(localeState).shortMonthOptions) + .map((month) => month.value) + .join('') ) { return '*'; } else if (isIncreasingSequence(months) && months.length !== 1) { @@ -162,22 +160,34 @@ export const cronExpState = atom( const res = validateCronExp(next); set(cronValidationErrorMessageState, res.hasError ? res.message : ''); if (!res.hasError) { + // Snapshot the cron currently implied by the field/period atoms *before* + // regenerating them. If the incoming value matches, this is a round-trip + // from a manual field/period edit and we must not re-derive the period + // (otherwise selecting a broad period on default fields would snap back). + const currentDerived = get(cronExpState); const cronParts = next.split(' '); generateMinute(cronParts[0], get(localeState), set as Setter); generateHour(cronParts[1], get(localeState), set as Setter); generateDayOfMonth(cronParts[2], get(localeState), set as Setter); generateMonth(cronParts[3], get(localeState), set as Setter); generateWeek(cronParts[4], get(localeState), set as Setter); - const period = get(periodState); - const periodOptions = getPeriodOptions(get(localeState).periodOptions); - if (cronParts[3] !== '*' && getPeriodIndex(period) < 4) { - set(periodState, periodOptions[4]); - } else if (cronParts[2] !== '*' && getPeriodIndex(period) < 3) { - set(periodState, periodOptions[3]); - } else if (cronParts[4] !== '*' && getPeriodIndex(period) < 2) { - set(periodState, periodOptions[2]); - } else if (cronParts[1] !== '*' && getPeriodIndex(period) < 1) { - set(periodState, periodOptions[1]); + if (next !== currentDerived) { + // External (prop/text) change: derive the period from the highest + // non-`*` segment and set it unconditionally so going from a narrow cron + // (e.g. `30 9 * * 1`) to a broad one (`* * * * *`) resets it instead of + // staying stuck on the old, now-stale period. + const periodOptions = getPeriodOptions(get(localeState).periodOptions); + let periodIndex = 0; + if (cronParts[3] !== '*') { + periodIndex = 4; + } else if (cronParts[2] !== '*') { + periodIndex = 3; + } else if (cronParts[4] !== '*') { + periodIndex = 2; + } else if (cronParts[1] !== '*') { + periodIndex = 1; + } + set(periodState, periodOptions[periodIndex]); } } }, @@ -197,6 +207,10 @@ const generateMinute = (part: string, locale: Locale, set: Setter) => { } else if (subparts[0] === '*') { set(minuteRangeStartSchedulerState, defaultMinuteOptionsWithOrdinal()[0]); set(minuteRangeEndSchedulerState, defaultMinuteOptionsWithOrdinal()[59]); + } else { + // Plain `n/m` (e.g. 1/4): start at n, end at the max. + set(minuteRangeStartSchedulerState, defaultMinuteOptionsWithOrdinal()[Number(subparts[0])]); + set(minuteRangeEndSchedulerState, defaultMinuteOptionsWithOrdinal()[59]); } set(minuteState, [DEFAULT_MINUTE_OPTS[Number(subparts[1])]]); } else if (part.indexOf('-') > 0) { @@ -232,6 +246,10 @@ const generateHour = (part: string, locale: Locale, set: Setter) => { } else if (subparts[0] === '*') { set(hourRangeStartSchedulerState, getTimesOfTheDay()[0]); set(hourRangeEndSchedulerState, getTimesOfTheDay()[23]); + } else { + // Plain `n/m` (e.g. 1/4): start at n, end at the max. + set(hourRangeStartSchedulerState, getTimesOfTheDay()[Number(subparts[0])]); + set(hourRangeEndSchedulerState, getTimesOfTheDay()[23]); } set(hourState, [DEFAULT_HOUR_OPTS_EVERY[Number(subparts[1])]]); } else if (part.indexOf('-') > 0) { @@ -273,6 +291,13 @@ const generateDayOfMonth = (part: string, locale: Locale, set: Setter) => { } else if (subparts[0] === '*') { set(dayOfMonthRangeStartSchedulerState, DEFAULT_DAY_OF_MONTH_OPTS_WITH_ORD[0]); set(dayOfMonthRangeEndSchedulerState, DEFAULT_DAY_OF_MONTH_OPTS_WITH_ORD[30]); + } else { + // Plain `n/m` (e.g. 1/4): start at n, end at the max. + set( + dayOfMonthRangeStartSchedulerState, + DEFAULT_DAY_OF_MONTH_OPTS_WITH_ORD[Number(subparts[0]) - 1], + ); + set(dayOfMonthRangeEndSchedulerState, DEFAULT_DAY_OF_MONTH_OPTS_WITH_ORD[30]); } set(dayOfMonthState, [DEFAULT_DAY_OF_MONTH_OPTS[Number(subparts[1]) - 1]]); } else if (part === 'L') { diff --git a/src/stories/scheduler.stories.tsx b/src/stories/scheduler.stories.tsx deleted file mode 100644 index d3720d1..0000000 --- a/src/stories/scheduler.stories.tsx +++ /dev/null @@ -1,76 +0,0 @@ -import { expect, userEvent, waitFor, within } from '@storybook/test'; -import React from 'react'; -import Scheduler from '../index'; - -export default { - title: 'Material UI Cron', - component: SchedulerDemo, -}; - -export function SchedulerDemo() { - const [cronExp, setCronExp] = React.useState('0 0 * * *'); - const [, setCronError] = React.useState(''); - const [isAdmin] = React.useState(true); - - return ( - - ); -} - -export const Default = () => ; - -Default.play = async ({ canvasElement }: { canvasElement: HTMLElement }) => { - const canvas = within(canvasElement); - - // Verify default cron expression is displayed - const cronInput = await canvas.findByDisplayValue('0 0 * * *'); - expect(cronInput).toBeInTheDocument(); - - // Verify key fields exist - expect(await canvas.findByLabelText('Period')).toHaveValue('day'); - expect(await canvas.findByLabelText('Minute(s)')).toBeInTheDocument(); - expect(await canvas.findByLabelText('Hour(s)')).toBeInTheDocument(); -}; - -function SchedulerNonAdminDemo() { - const [cronExp, setCronExp] = React.useState('0 0 * * *'); - const [, setCronError] = React.useState(''); - return ( - - ); -} - -export const NonAdmin = () => ; - -NonAdmin.play = async ({ canvasElement }: { canvasElement: HTMLElement }) => { - const canvas = within(canvasElement); - - // Verify cron input is disabled for non-admin - const cronInput = await canvas.findByDisplayValue('0 0 * * *'); - expect(cronInput).toBeDisabled(); -}; - -function SchedulerPropChangeDemo() { - const [cronExp, setCronExp] = React.useState('0 0 * * *'); - const [, setCronError] = React.useState(''); - return ( -
- - -
- ); -} - -export const PropChange = () => ; - -PropChange.play = async ({ canvasElement }: { canvasElement: HTMLElement }) => { - const canvas = within(canvasElement); - const user = userEvent.setup(); - - // Test that external prop changes sync to the component - const btn = await canvas.findByRole('button', { name: 'Apply */5 * * * *' }); - await user.click(btn); - await waitFor(() => expect(canvas.getByDisplayValue('*/5 * * * *')).toBeInTheDocument()); -}; diff --git a/src/utils.test.ts b/src/utils.test.ts new file mode 100644 index 0000000..aa4ae10 --- /dev/null +++ b/src/utils.test.ts @@ -0,0 +1,265 @@ +import { describe, expect, it } from 'vitest'; +import type { SelectOptions } from './types'; +import { + countOccurrences, + doesNumberStartWithZero, + getNumbersInCronPart, + getPeriodIndex, + getSortedOptions, + hasNoDuplicates, + hasValidCronParts, + hasValidNumbersInCronPart, + isAscending, + isIncreasingSequence, + isValidDayOfMonthPart, + isValidDayOfWeekPart, + isValidHourPart, + isValidMinutePart, + isValidMonthPart, + isValidStepPart, + range, + validateCronExp, +} from './utils'; + +const opt = (value: string): SelectOptions => ({ value, label: value }); + +describe('hasValidCronParts', () => { + it('accepts exactly five space-separated parts', () => { + expect(hasValidCronParts('* * * * *')).toBe(true); + expect(hasValidCronParts('0 0 1 1 0')).toBe(true); + }); + + it('rejects the wrong number of parts', () => { + expect(hasValidCronParts('* * * *')).toBe(false); + expect(hasValidCronParts('* * * * * *')).toBe(false); + }); +}); + +describe('getNumbersInCronPart', () => { + it('extracts every numeric token', () => { + expect(getNumbersInCronPart('1-10/4')).toEqual([1, 10, 4]); + expect(getNumbersInCronPart('1,2,3')).toEqual([1, 2, 3]); + expect(getNumbersInCronPart('*')).toEqual([]); + expect(getNumbersInCronPart('5')).toEqual([5]); + }); +}); + +describe('doesNumberStartWithZero', () => { + it('flags multi-digit numbers with a leading zero', () => { + expect(doesNumberStartWithZero('01')).toBe(true); + expect(doesNumberStartWithZero('00')).toBe(true); + expect(doesNumberStartWithZero('0-05')).toBe(true); + }); + + it('does not flag a bare zero or normal numbers', () => { + expect(doesNumberStartWithZero('0')).toBe(false); + expect(doesNumberStartWithZero('10')).toBe(false); + expect(doesNumberStartWithZero('1-10')).toBe(false); + }); +}); + +describe('hasNoDuplicates', () => { + it('detects duplicate numbers within a list', () => { + expect(hasNoDuplicates('1,2,3')).toBe(true); + expect(hasNoDuplicates('1,2,2')).toBe(false); + }); + + it('treats slash sub-parts independently', () => { + expect(hasNoDuplicates('1-10/4')).toBe(true); + }); +}); + +describe('hasValidNumbersInCronPart', () => { + it('applies the predicate to every number', () => { + expect(hasValidNumbersInCronPart('1,2,3', (n) => n >= 0 && n <= 59)).toBe(true); + expect(hasValidNumbersInCronPart('1,60', (n) => n >= 0 && n <= 59)).toBe(false); + }); +}); + +describe('isAscending', () => { + it('is true for non-decreasing sequences', () => { + expect(isAscending(['1', '2', '2', '3'])).toBe(true); + expect(isAscending(['5'])).toBe(true); + }); + + it('is false when a later value is smaller', () => { + expect(isAscending(['3', '1'])).toBe(false); + }); +}); + +describe('isIncreasingSequence', () => { + it('is true for a contiguous run', () => { + expect(isIncreasingSequence([opt('1'), opt('2'), opt('3')])).toBe(true); + }); + + it('is false when there is a gap', () => { + expect(isIncreasingSequence([opt('1'), opt('3')])).toBe(false); + }); +}); + +describe('getSortedOptions', () => { + it('sorts options numerically by value', () => { + expect(getSortedOptions([opt('10'), opt('2'), opt('1')]).map((o) => o.value)).toEqual([ + '1', + '2', + '10', + ]); + }); +}); + +describe('getPeriodIndex', () => { + it('maps period values to their index', () => { + expect(getPeriodIndex(opt('hour'))).toBe(0); + expect(getPeriodIndex(opt('day'))).toBe(1); + expect(getPeriodIndex(opt('week'))).toBe(2); + expect(getPeriodIndex(opt('month'))).toBe(3); + expect(getPeriodIndex(opt('year'))).toBe(4); + }); +}); + +describe('countOccurrences', () => { + it('counts matching entries', () => { + expect(countOccurrences(['a', 'b', 'a'], 'a')).toBe(2); + expect(countOccurrences([], 'a')).toBe(0); + }); +}); + +describe('range', () => { + it('builds an inclusive string range', () => { + expect(range(0, 4)).toEqual(['0', '1', '2', '3', '4']); + }); + + it('honours a custom step', () => { + expect(range(0, 10, 5)).toEqual(['0', '5', '10']); + }); +}); + +describe('isValidStepPart (#19 step validation)', () => { + it('accepts `*/n`', () => { + expect(isValidStepPart('*/4').isValid).toBe(true); + expect(isValidStepPart('*/15').isValid).toBe(true); + }); + + it('accepts plain `n/m` — the #19 regression', () => { + expect(isValidStepPart('1/4').isValid).toBe(true); + expect(isValidStepPart('5/15').isValid).toBe(true); + }); + + it('keeps the existing step-value constraint (no `0` digit in the step)', () => { + // REGEX_EVERY's step group is [1-9]{1,2}, so steps like 10/20 are rejected. + // This is pre-existing behaviour, documented here so the boundary is intentional. + expect(isValidStepPart('5/10').isValid).toBe(false); + }); + + it('accepts an ascending `x-y/n` range step', () => { + expect(isValidStepPart('1-10/4').isValid).toBe(true); + }); + + it('rejects a descending range step', () => { + const res = isValidStepPart('10-1/4'); + expect(res.isValid).toBe(false); + expect(res.message).toBe('Incorrect range'); + }); + + it('rejects malformed step parts', () => { + expect(isValidStepPart('1/').isValid).toBe(false); + expect(isValidStepPart('*/0').isValid).toBe(false); + }); +}); + +describe('isValidMinutePart', () => { + it.each(['*', '5', '0', '1,2,3', '1-30', '*/5', '0/15', '1-30/5'])('accepts %s', (part) => { + expect(isValidMinutePart(`${part} * * * *`).isValid).toBe(true); + }); + + it('rejects out-of-range minutes', () => { + expect(isValidMinutePart('60 * * * *').isValid).toBe(false); + }); + + it('rejects leading zero', () => { + expect(isValidMinutePart('05 * * * *').isValid).toBe(false); + }); + + it('rejects duplicates', () => { + expect(isValidMinutePart('1,1 * * * *').isValid).toBe(false); + }); + + it('rejects a descending range', () => { + expect(isValidMinutePart('30-10 * * * *').isValid).toBe(false); + }); +}); + +describe('isValidHourPart', () => { + it.each(['*', '9', '1/4', '*/4', '1-10/4'])('accepts %s', (part) => { + expect(isValidHourPart(`0 ${part} * * *`).isValid).toBe(true); + }); + + it('rejects out-of-range hours', () => { + expect(isValidHourPart('0 24 * * *').isValid).toBe(false); + }); +}); + +describe('isValidDayOfMonthPart', () => { + it.each(['*', '1', '15', 'L', '1-15', '1/5'])('accepts %s', (part) => { + expect(isValidDayOfMonthPart(`0 0 ${part} * *`).isValid).toBe(true); + }); + + it('rejects day 0 and out-of-range days', () => { + expect(isValidDayOfMonthPart('0 0 0 * *').isValid).toBe(false); + expect(isValidDayOfMonthPart('0 0 32 * *').isValid).toBe(false); + }); +}); + +describe('isValidMonthPart', () => { + it.each(['*', '1', '12', '1,6,12', '1-6'])('accepts %s', (part) => { + expect(isValidMonthPart(`0 0 1 ${part} *`).isValid).toBe(true); + }); + + it('rejects out-of-range months', () => { + expect(isValidMonthPart('0 0 1 13 *').isValid).toBe(false); + }); +}); + +describe('isValidDayOfWeekPart', () => { + it.each(['*', '0', '6', '1-5', '1,3,5'])('accepts %s', (part) => { + expect(isValidDayOfWeekPart(`0 0 * * ${part}`).isValid).toBe(true); + }); + + it('rejects out-of-range weekdays', () => { + expect(isValidDayOfWeekPart('0 0 * * 7').isValid).toBe(false); + }); +}); + +describe('validateCronExp (end-to-end)', () => { + const valid = [ + '* * * * *', + '0 0 * * *', + '0 1/4 * * *', // #19 + '*/5 * * * *', + '0 */4 * * *', + '0 1-10/4 * * *', + '0 0 1/5 * *', + '0 0 1-15 * *', + '30 9 * * 1', + '0 0 L * *', + '0 0 1 1 0', + ]; + it.each(valid)('accepts %s', (cron) => { + expect(validateCronExp(cron).hasError).toBe(false); + }); + + const invalid: [string, string][] = [ + ['0 1/4 * *', 'Cron should have 5 parts'], + ['60 * * * *', 'Invalid minute cron part: Number should be between 0 and 59'], + ['0 24 * * *', 'Invalid hour cron part: Number should be between 0 and 23'], + ['0 10-1/4 * * *', 'Invalid hour cron part: Incorrect range'], + ['0 0 32 * *', 'Invalid day of month cron part: Number should be between 1 and 31'], + ['0 0 1 13 *', 'Invalid month cron part: Number should be between 0 and 6'], + ['0 0 * * 7', 'Invalid day of week cron part: Number should be between 0 and 6'], + ]; + it.each(invalid)('rejects %s', (cron, message) => { + const res = validateCronExp(cron); + expect(res.hasError).toBe(true); + expect(res.message).toBe(message); + }); +}); diff --git a/src/utils.ts b/src/utils.ts index 0bbd960..f411280 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -126,6 +126,24 @@ export const CRON_VALIDATION = (isValid: boolean, message: string): CronValidati }; }; +// Validates a step part (`a/b`) by branching on what comes before the slash: +// `*` -> `*/n` (REGEX_ALL, e.g. */4) +// `x-y` -> `x-y/n` (REGEX_EVERY_HYPEN, e.g. 1-10/4) when ascending +// `n` -> `n/m` (REGEX_EVERY, e.g. 1/4) +// The minute/hour/day-of-month parts share identical step semantics. +export const isValidStepPart = (part: string): CronValidation => { + const beforeSlash = part.split('/')[0]; + if (beforeSlash === '*') { + return CRON_VALIDATION(REGEX_ALL.test(part), 'Incorrect syntax'); + } else if (beforeSlash.indexOf('-') > 0) { + if (isAscending(beforeSlash.split('-'))) { + return CRON_VALIDATION(REGEX_EVERY_HYPEN.test(part), 'Incorrect syntax hypen'); + } + return CRON_VALIDATION(false, 'Incorrect range'); + } + return CRON_VALIDATION(REGEX_EVERY.test(part), 'Incorrect syntax'); +}; + export const isValidMinutePart = (cronExp: string) => { const part = cronExp.split(' ')[0]; if (doesNumberStartWithZero(part)) { @@ -135,13 +153,7 @@ export const isValidMinutePart = (cronExp: string) => { } else if (!hasNoDuplicates(part)) { return CRON_VALIDATION(false, 'Duplicate numbers not allowed'); } else if (part.indexOf('/') > 0) { - if (part.split('/')[0] !== '*' && isAscending(part.split('/')[0].split('-'))) { - return CRON_VALIDATION(REGEX_EVERY_HYPEN.test(part), 'Incorrect syntax hypen'); - } else if (part.indexOf('-') > 0) { - return CRON_VALIDATION(false, 'Incorrect range'); - } else { - return CRON_VALIDATION(REGEX_ALL.test(part), 'Incorrect syntax'); - } + return isValidStepPart(part); } else if (part.indexOf(',') > 0) { return CRON_VALIDATION(REGEX_COMMA.test(part), 'Invalid syntax'); } else if (part.indexOf('-') > 0) { @@ -165,13 +177,7 @@ export const isValidHourPart = (cronExp: string) => { } else if (!hasNoDuplicates(part)) { return CRON_VALIDATION(false, 'Duplicate numbers not allowed'); } else if (part.indexOf('/') > 0) { - if (part.split('/')[0] !== '*' && isAscending(part.split('/')[0].split('-'))) { - return CRON_VALIDATION(REGEX_EVERY_HYPEN.test(part), 'Incorrect syntax hypen'); - } else if (part.indexOf('-') > 0) { - return CRON_VALIDATION(false, 'Incorrect range'); - } else { - return CRON_VALIDATION(REGEX_ALL.test(part), 'Incorrect syntax'); - } + return isValidStepPart(part); } else if (part.indexOf(',') > 0) { return CRON_VALIDATION(REGEX_COMMA.test(part), 'Invalid syntax'); } else if (part.indexOf('-') > 0) { @@ -195,13 +201,7 @@ export const isValidDayOfMonthPart = (cronExp: string) => { } else if (!hasNoDuplicates(part)) { return CRON_VALIDATION(false, 'Duplicate numbers not allowed'); } else if (part.indexOf('/') > 0) { - if (part.split('/')[0] !== '*' && isAscending(part.split('/')[0].split('-'))) { - return CRON_VALIDATION(REGEX_EVERY_HYPEN.test(part), 'Incorrect syntax hypen'); - } else if (part.indexOf('-') > 0) { - return CRON_VALIDATION(false, 'Incorrect range'); - } else { - return CRON_VALIDATION(REGEX_ALL.test(part), 'Incorrect syntax'); - } + return isValidStepPart(part); } else if (part.indexOf(',') > 0) { return CRON_VALIDATION(REGEX_COMMA.test(part), 'Invalid syntax'); } else if (part.indexOf('-') > 0) { diff --git a/tsconfig.json b/tsconfig.json index 64b5c3c..2a03327 100755 --- a/tsconfig.json +++ b/tsconfig.json @@ -22,5 +22,5 @@ "noEmit": false }, "include": ["./src/**/*"], - "exclude": ["node_modules", "dist", "storybook-static"] + "exclude": ["node_modules", "dist", "demo"] } diff --git a/vercel.json b/vercel.json new file mode 100644 index 0000000..78d81bb --- /dev/null +++ b/vercel.json @@ -0,0 +1,8 @@ +{ + "$schema": "https://openapi.vercel.sh/vercel.json", + "framework": "vite", + "installCommand": "yarn install --frozen-lockfile && yarn --cwd demo install", + "buildCommand": "yarn --cwd demo build", + "outputDirectory": "demo/dist", + "rewrites": [{ "source": "/(.*)", "destination": "/index.html" }] +} diff --git a/vitest.config.mts b/vitest.config.mts new file mode 100644 index 0000000..360a426 --- /dev/null +++ b/vitest.config.mts @@ -0,0 +1,49 @@ +/// +import react from '@vitejs/plugin-react'; +import { defineConfig } from 'vitest/config'; + +export default defineConfig({ + test: { + projects: [ + { + // Pure-logic unit tests (cron validation, selector derivations) run in Node. + test: { + name: 'unit', + environment: 'node', + include: ['src/**/*.test.ts'], + exclude: ['src/**/*.browser.test.*'], + }, + }, + { + // Component tests render the real in a real browser. jsdom is not + // viable here — MUI's Autocomplete triggers an infinite update loop under jsdom. + plugins: [react()], + test: { + name: 'browser', + include: ['src/**/*.browser.test.tsx'], + setupFiles: ['./vitest.setup.browser.ts'], + browser: { + enabled: true, + provider: 'playwright', + headless: true, + instances: [{ browser: 'chromium' }], + }, + }, + }, + ], + coverage: { + provider: 'v8', + include: ['src/**/*.{ts,tsx}'], + exclude: [ + 'src/**/*.test.*', + 'src/**/*.browser.test.*', + 'src/**/*.stories.*', + 'src/**/*.d.ts', + 'src/index.ts', + 'src/types.ts', + ], + reporter: ['text', 'text-summary', 'html', 'json-summary'], + reportsDirectory: './coverage', + }, + }, +}); diff --git a/vitest.setup.browser.ts b/vitest.setup.browser.ts new file mode 100644 index 0000000..bb02c60 --- /dev/null +++ b/vitest.setup.browser.ts @@ -0,0 +1 @@ +import '@testing-library/jest-dom/vitest'; diff --git a/yarn.lock b/yarn.lock index f2e8d8d..3322404 100644 --- a/yarn.lock +++ b/yarn.lock @@ -7,6 +7,14 @@ resolved "https://registry.yarnpkg.com/@adobe/css-tools/-/css-tools-4.4.4.tgz#2856c55443d3d461693f32d2b96fb6ea92e1ffa9" integrity sha512-Elp+iwUx5rN5+Y8xLt5/GRoG20WGoDCQ/1Fb+1LiGtvwbDavuSk0jhD/eZdckHAuzcDzccnkv+rEjyWfRx18gg== +"@ampproject/remapping@^2.3.0": + version "2.3.0" + resolved "https://registry.yarnpkg.com/@ampproject/remapping/-/remapping-2.3.0.tgz#ed441b6fa600072520ce18b43d2c8cc8caecc7f4" + integrity sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw== + dependencies: + "@jridgewell/gen-mapping" "^0.3.5" + "@jridgewell/trace-mapping" "^0.3.24" + "@babel/code-frame@^7.0.0": version "7.14.5" resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.14.5.tgz#23b08d740e83f49c5e59945fbf1b43e80bbf4edb" @@ -103,6 +111,11 @@ resolved "https://registry.yarnpkg.com/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz#54da796097ab19ce67ed9f88b47bb2ec49367687" integrity sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA== +"@babel/helper-string-parser@^7.29.7": + version "7.29.7" + resolved "https://registry.yarnpkg.com/@babel/helper-string-parser/-/helper-string-parser-7.29.7.tgz#7f0871d99824d23137d60f86fcf6130fd5a1b51f" + integrity sha512-Pb5ijPrZ89GDH8223L4UP8i6QApWxs04RbPQJTeWDV0/keR2E36MeKnyr6LYmUUvqRRI+Iv87SuF1W6ErINzYw== + "@babel/helper-validator-identifier@^7.14.5": version "7.14.5" resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.14.5.tgz#d0f0e277c512e0c938277faa85a3968c9a44c0e8" @@ -113,6 +126,11 @@ resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.27.1.tgz#a7054dcc145a967dd4dc8fee845a57c1316c9df8" integrity sha512-D2hP9eA+Sqx1kBZgzxZh0y1trbuU+JoDkiEwqhQ36nodYqJwyEIhPSdMNd7lOm/4io72luTPWH20Yda0xOuUow== +"@babel/helper-validator-identifier@^7.29.7": + version "7.29.7" + resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.29.7.tgz#bd87084ced0c796ec46bda492de6e83d29e89fc2" + integrity sha512-qehxGkRj55h/ff8EMaJ+cYhyaKlHIxqYDn682wQD7RNp9UujOQsHog2uS0r2vzr4pW+sXf90NeeayjcNaX3fFg== + "@babel/helper-validator-option@^7.27.1": version "7.27.1" resolved "https://registry.yarnpkg.com/@babel/helper-validator-option/-/helper-validator-option-7.27.1.tgz#fa52f5b1e7db1ab049445b421c4471303897702f" @@ -142,6 +160,13 @@ dependencies: "@babel/types" "^7.28.4" +"@babel/parser@^7.25.4": + version "7.29.7" + resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.29.7.tgz#837b87387cbf5ec5530cb634b3c622f68edb9334" + integrity sha512-hnORnjP/1P/zFEndoeX+n+t1RwWRJiJpM/jO7FW32Kn9r5+sJB2JWOdYo4L6k78j15eCwY3Gm/7364B1EMwtNg== + dependencies: + "@babel/types" "^7.29.7" + "@babel/plugin-transform-react-jsx-self@^7.27.1": version "7.27.1" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-jsx-self/-/plugin-transform-react-jsx-self-7.27.1.tgz#af678d8506acf52c577cac73ff7fe6615c85fc92" @@ -198,6 +223,19 @@ "@babel/helper-string-parser" "^7.27.1" "@babel/helper-validator-identifier" "^7.27.1" +"@babel/types@^7.25.4", "@babel/types@^7.29.7": + version "7.29.7" + resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.29.7.tgz#8005e31d82712ee7adaef6e23c63b71a62770a92" + integrity sha512-4zBIxpPzowiZpusoFkyGVwakdRJUyuH5PxQ/PrqghfdFWWasvnCdPfQXHrenDai+gyLARulZjZowCOj6fjT4pA== + dependencies: + "@babel/helper-string-parser" "^7.29.7" + "@babel/helper-validator-identifier" "^7.29.7" + +"@bcoe/v8-coverage@^1.0.2": + version "1.0.2" + resolved "https://registry.yarnpkg.com/@bcoe/v8-coverage/-/v8-coverage-1.0.2.tgz#bbe12dca5b4ef983a0d0af4b07b9bc90ea0ababa" + integrity sha512-6zABk/ECA/QYSCQ1NGiVwwbQerUCZ+TQbp64Q3AgmfNvurHH0j8TtXa1qbShXA6qqkpAj4V5W8pP6mLe1mcMqA== + "@biomejs/biome@^2.2.5": version "2.2.5" resolved "https://registry.yarnpkg.com/@biomejs/biome/-/biome-2.2.5.tgz#291df1137404843537dd50eaa0174c12c0681d82" @@ -643,6 +681,11 @@ wrap-ansi "^8.1.0" wrap-ansi-cjs "npm:wrap-ansi@^7.0.0" +"@istanbuljs/schema@^0.1.2": + version "0.1.6" + resolved "https://registry.yarnpkg.com/@istanbuljs/schema/-/schema-0.1.6.tgz#8dc9afa2ac1506cb1a58f89940f1c124446c8df3" + integrity sha512-+Sg6GCR/wy1oSmQDFq4LQDAhm3ETKnorxN+y5nbLULOR3P0c14f2Wurzj3/xqPXtasLFfHd5iRFQ7AJt4KH2cw== + "@joshwooding/vite-plugin-react-docgen-typescript@0.6.1": version "0.6.1" resolved "https://registry.yarnpkg.com/@joshwooding/vite-plugin-react-docgen-typescript/-/vite-plugin-react-docgen-typescript-0.6.1.tgz#f630b93ed13d5d07483c0ead42db793053b364a9" @@ -678,6 +721,14 @@ resolved "https://registry.yarnpkg.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz#6912b00d2c631c0d15ce1a7ab57cd657f2a8f8ba" integrity sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og== +"@jridgewell/trace-mapping@^0.3.23", "@jridgewell/trace-mapping@^0.3.31": + version "0.3.31" + resolved "https://registry.yarnpkg.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz#db15d6781c931f3a251a3dac39501c98a6082fd0" + integrity sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw== + dependencies: + "@jridgewell/resolve-uri" "^3.1.0" + "@jridgewell/sourcemap-codec" "^1.4.14" + "@jridgewell/trace-mapping@^0.3.24", "@jridgewell/trace-mapping@^0.3.28": version "0.3.30" resolved "https://registry.yarnpkg.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.30.tgz#4a76c4daeee5df09f5d3940e087442fb36ce2b99" @@ -818,6 +869,11 @@ resolved "https://registry.yarnpkg.com/@pkgjs/parseargs/-/parseargs-0.11.0.tgz#a77ea742fab25775145434eb1d2328cf5013ac33" integrity sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg== +"@polka/url@^1.0.0-next.24": + version "1.0.0-next.29" + resolved "https://registry.yarnpkg.com/@polka/url/-/url-1.0.0-next.29.tgz#5a40109a1ab5f84d6fd8fc928b19f367cbe7e7b1" + integrity sha512-wwQAWhWSuHaag8c4q/KN/vCoeOJYshAIvMQwD4GpSb3OiZklFfvAgmj0VCBBImRpuF/aFgIRzllXlVX93Jevww== + "@popperjs/core@^2.11.8": version "2.11.8" resolved "https://registry.yarnpkg.com/@popperjs/core/-/core-2.11.8.tgz#6b79032e760a0899cd4204710beede972a3a185f" @@ -1077,6 +1133,20 @@ lz-string "^1.5.0" pretty-format "^27.0.2" +"@testing-library/dom@^10", "@testing-library/dom@^10.4.0": + version "10.4.1" + resolved "https://registry.yarnpkg.com/@testing-library/dom/-/dom-10.4.1.tgz#d444f8a889e9a46e9a3b4f3b88e0fcb3efb6cf95" + integrity sha512-o4PXJQidqJl82ckFaXUeoAW+XysPLauYI43Abki5hABd853iMhitooc6znOnczgbTYmEP6U6/y1ZyKAIsvMKGg== + dependencies: + "@babel/code-frame" "^7.10.4" + "@babel/runtime" "^7.12.5" + "@types/aria-query" "^5.0.1" + aria-query "5.3.0" + dom-accessibility-api "^0.5.9" + lz-string "^1.5.0" + picocolors "1.1.1" + pretty-format "^27.0.2" + "@testing-library/jest-dom@6.5.0": version "6.5.0" resolved "https://registry.yarnpkg.com/@testing-library/jest-dom/-/jest-dom-6.5.0.tgz#50484da3f80fb222a853479f618a9ce5c47bfe54" @@ -1090,7 +1160,7 @@ lodash "^4.17.21" redent "^3.0.0" -"@testing-library/jest-dom@^6.6.3": +"@testing-library/jest-dom@^6", "@testing-library/jest-dom@^6.6.3": version "6.9.1" resolved "https://registry.yarnpkg.com/@testing-library/jest-dom/-/jest-dom-6.9.1.tgz#7613a04e146dd2976d24ddf019730d57a89d56c2" integrity sha512-zIcONa+hVtVSSep9UT3jZ5rizo2BsxgyDYU7WFD5eICBE7no3881HGeb/QkGfsJs6JTkY1aQhT7rIPC7e+0nnA== @@ -1102,12 +1172,19 @@ picocolors "^1.1.1" redent "^3.0.0" +"@testing-library/react@^16": + version "16.3.2" + resolved "https://registry.yarnpkg.com/@testing-library/react/-/react-16.3.2.tgz#672883b7acb8e775fc0492d9e9d25e06e89786d0" + integrity sha512-XU5/SytQM+ykqMnAnvB2umaJNIOsLF3PVv//1Ew4CTcpz0/BRyy/af40qqrt7SjKpDdT1saBMc42CUok5gaw+g== + dependencies: + "@babel/runtime" "^7.12.5" + "@testing-library/user-event@14.5.2": version "14.5.2" resolved "https://registry.yarnpkg.com/@testing-library/user-event/-/user-event-14.5.2.tgz#db7257d727c891905947bd1c1a99da20e03c2ebd" integrity sha512-YAh82Wh4TIrxYLmfGcixwD18oIjyC1pFQC2Y01F2lzV2HTMiYrI0nze0FD0ocB//CKS/7jIUgae+adPqxK5yCQ== -"@testing-library/user-event@^14.6.1": +"@testing-library/user-event@^14", "@testing-library/user-event@^14.6.1": version "14.6.1" resolved "https://registry.yarnpkg.com/@testing-library/user-event/-/user-event-14.6.1.tgz#13e09a32d7a8b7060fe38304788ebf4197cd2149" integrity sha512-vq7fv0rnt+QTXgPxr5Hjc210p6YKq2kmdziLgnsZGgLJ9e6VAShx1pACLuRjd/AS/sr7phAR58OIIpf0LlmQNw== @@ -1228,6 +1305,39 @@ "@types/babel__core" "^7.20.5" react-refresh "^0.17.0" +"@vitest/browser@^3": + version "3.2.4" + resolved "https://registry.yarnpkg.com/@vitest/browser/-/browser-3.2.4.tgz#4238600dc8343b8a9c032266c743f7d38ae3da84" + integrity sha512-tJxiPrWmzH8a+w9nLKlQMzAKX/7VjFs50MWgcAj7p9XQ7AQ9/35fByFYptgPELyLw+0aixTnC4pUWV+APcZ/kw== + dependencies: + "@testing-library/dom" "^10.4.0" + "@testing-library/user-event" "^14.6.1" + "@vitest/mocker" "3.2.4" + "@vitest/utils" "3.2.4" + magic-string "^0.30.17" + sirv "^3.0.1" + tinyrainbow "^2.0.0" + ws "^8.18.2" + +"@vitest/coverage-v8@^3": + version "3.2.4" + resolved "https://registry.yarnpkg.com/@vitest/coverage-v8/-/coverage-v8-3.2.4.tgz#a2d8d040288c1956a1c7d0a0e2cdcfc7a3319f13" + integrity sha512-EyF9SXU6kS5Ku/U82E259WSnvg6c8KTjppUncuNdm5QHpe17mwREHnjDzozC8x9MZ0xfBUFSaLkRv4TMA75ALQ== + 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" + "@vitest/expect@2.0.5": version "2.0.5" resolved "https://registry.yarnpkg.com/@vitest/expect/-/expect-2.0.5.tgz#f3745a6a2c18acbea4d39f5935e913f40d26fa86" @@ -1272,13 +1382,31 @@ dependencies: tinyrainbow "^1.2.0" -"@vitest/pretty-format@3.2.4": +"@vitest/pretty-format@3.2.4", "@vitest/pretty-format@^3.2.4": version "3.2.4" resolved "https://registry.yarnpkg.com/@vitest/pretty-format/-/pretty-format-3.2.4.tgz#3c102f79e82b204a26c7a5921bf47d534919d3b4" integrity sha512-IVNZik8IVRJRTr9fxlitMKeJeXFFFN0JaB9PHPGQ8NKQbGpfjlTx9zO4RefN8gp7eqjNy8nyK3NZmBzOPeIxtA== dependencies: tinyrainbow "^2.0.0" +"@vitest/runner@3.2.4": + version "3.2.4" + resolved "https://registry.yarnpkg.com/@vitest/runner/-/runner-3.2.4.tgz#5ce0274f24a971f6500f6fc166d53d8382430766" + integrity sha512-oukfKT9Mk41LreEW09vt45f8wx7DordoWUZMYdY/cyAk7w5TWkTRCNZYF7sX7n2wB7jyGAl74OxgwhPgKaqDMQ== + dependencies: + "@vitest/utils" "3.2.4" + pathe "^2.0.3" + strip-literal "^3.0.0" + +"@vitest/snapshot@3.2.4": + version "3.2.4" + resolved "https://registry.yarnpkg.com/@vitest/snapshot/-/snapshot-3.2.4.tgz#40a8bc0346ac0aee923c0eefc2dc005d90bc987c" + integrity sha512-dEYtS7qQP2CjU27QBC5oUOxLE/v5eLkGqPE0ZKEIDGMs4vKWe7IjgLOeauHsR0D5YuuycGRO5oSRXnwnmA78fQ== + dependencies: + "@vitest/pretty-format" "3.2.4" + magic-string "^0.30.17" + pathe "^2.0.3" + "@vitest/spy@2.0.5": version "2.0.5" resolved "https://registry.yarnpkg.com/@vitest/spy/-/spy-2.0.5.tgz#590fc07df84a78b8e9dd976ec2090920084a2b9f" @@ -1505,6 +1633,15 @@ ast-types@^0.16.1: dependencies: tslib "^2.0.1" +ast-v8-to-istanbul@^0.3.3: + version "0.3.12" + resolved "https://registry.yarnpkg.com/ast-v8-to-istanbul/-/ast-v8-to-istanbul-0.3.12.tgz#8eb1b7c86ef8499859be761b17ffd91406c0c36f" + integrity sha512-BRRC8VRZY2R4Z4lFIL35MwNXmwVqBityvOIwETtsCSwvjl0IdgFsy9NhdaA6j74nUdtJJlIypeRhpDam19Wq3g== + dependencies: + "@jridgewell/trace-mapping" "^0.3.31" + estree-walker "^3.0.3" + js-tokens "^10.0.0" + babel-plugin-macros@^3.1.0: version "3.1.0" resolved "https://registry.yarnpkg.com/babel-plugin-macros/-/babel-plugin-macros-3.1.0.tgz#9ef6dc74deb934b4db344dc973ee851d148c50c1" @@ -1519,6 +1656,11 @@ balanced-match@^1.0.0: resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.2.tgz#e83e3a7e3f300b34cb9d87f615fa0cbf357690ee" integrity sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw== +balanced-match@^4.0.2: + version "4.0.4" + resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-4.0.4.tgz#bfb10662feed8196a2c62e7c68e17720c274179a" + integrity sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA== + better-opn@^3.0.2: version "3.0.2" resolved "https://registry.yarnpkg.com/better-opn/-/better-opn-3.0.2.tgz#f96f35deaaf8f34144a4102651babcf00d1d8817" @@ -1533,6 +1675,13 @@ brace-expansion@^2.0.1: dependencies: balanced-match "^1.0.0" +brace-expansion@^5.0.5: + version "5.0.6" + resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-5.0.6.tgz#ec68fe0a641a29d8711579caf641d05bae1f2285" + integrity sha512-kLpxurY4Z4r9sgMsyG0Z9uzsBlgiU/EFKhj/h91/8yHu0edo7XuixOIH3VcJ8kkxs6/jPzoI6U9Vj3WqbMQ94g== + dependencies: + balanced-match "^4.0.2" + browserslist@^4.24.0: version "4.25.4" resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.25.4.tgz#ebdd0e1d1cf3911834bab3a6cd7b917d9babf5af" @@ -1543,6 +1692,11 @@ browserslist@^4.24.0: node-releases "^2.0.19" update-browserslist-db "^1.1.3" +cac@^6.7.14: + version "6.7.14" + resolved "https://registry.yarnpkg.com/cac/-/cac-6.7.14.tgz#804e1e6f506ee363cb0e3ccbb09cad5dd9870959" + integrity sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ== + callsites@^3.0.0: version "3.1.0" resolved "https://registry.yarnpkg.com/callsites/-/callsites-3.1.0.tgz#b3630abd8943432f54b3f0519238e33cd7df2f73" @@ -1702,6 +1856,13 @@ debug@^4.1.0, debug@^4.3.1: dependencies: ms "2.1.2" +debug@^4.1.1, debug@^4.4.1: + version "4.4.3" + resolved "https://registry.yarnpkg.com/debug/-/debug-4.4.3.tgz#c6ae432d9bd9662582fce08709b038c58e9e3d6a" + integrity sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA== + dependencies: + ms "^2.1.3" + debug@^4.3.4, debug@^4.4.0: version "4.4.1" resolved "https://registry.yarnpkg.com/debug/-/debug-4.4.1.tgz#e5a8bc6cbc4c6cd3e64308b0693a3d4fa550189b" @@ -1781,6 +1942,11 @@ error-ex@^1.3.1: dependencies: is-arrayish "^0.2.1" +es-module-lexer@^1.7.0: + version "1.7.0" + resolved "https://registry.yarnpkg.com/es-module-lexer/-/es-module-lexer-1.7.0.tgz#9159601561880a85f2734560a9099b2c31e5372a" + integrity sha512-jEQoCwk8hyb2AZziIOLhDqpm5+2ww5uIE6lkO/6jcOCusfk6LhMHpXXfBLXTZ7Ydyt0j4VoUQv6uGNYbdW+kBA== + esbuild-register@^3.5.0: version "3.6.0" resolved "https://registry.yarnpkg.com/esbuild-register/-/esbuild-register-3.6.0.tgz#cf270cfa677baebbc0010ac024b823cbf723a36d" @@ -1889,6 +2055,11 @@ esutils@^2.0.2: resolved "https://registry.yarnpkg.com/esutils/-/esutils-2.0.3.tgz#74d2eb4de0b8da1293711910d50775b9b710ef64" integrity sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g== +expect-type@^1.2.1: + version "1.3.0" + resolved "https://registry.yarnpkg.com/expect-type/-/expect-type-1.3.0.tgz#0d58ed361877a31bbc4dd6cf71bbfef7faf6bd68" + integrity sha512-knvyeauYhqjOYvQ66MznSMs83wmHrCycNEN6Ao+2AeYEfxUIkuiVxdEa1qlGEPK+We3n0THiDciYSsCcgW/DoA== + exsolve@^1.0.7: version "1.0.7" resolved "https://registry.yarnpkg.com/exsolve/-/exsolve-1.0.7.tgz#3b74e4c7ca5c5f9a19c3626ca857309fa99f9e9e" @@ -1940,7 +2111,7 @@ fs-extra@~11.3.0: jsonfile "^6.0.1" universalify "^2.0.0" -fsevents@~2.3.2: +fsevents@2.3.2, fsevents@~2.3.2: version "2.3.2" resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.3.2.tgz#8a526f78b8fdf4623b709e0b975c52c24c02fd1a" integrity sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA== @@ -1977,6 +2148,18 @@ glob@^10.0.0: package-json-from-dist "^1.0.0" path-scurry "^1.11.1" +glob@^10.4.1: + version "10.5.0" + resolved "https://registry.yarnpkg.com/glob/-/glob-10.5.0.tgz#8ec0355919cd3338c28428a23d4f24ecc5fe738c" + integrity sha512-DfXN8DfhJ7NH3Oe7cFmu3NCu1wKbkReJ8TorzSAFbSKrlNaQSKfIzqYqVY8zlbs2NLBbWpRiU52GX2PbaBVNkg== + 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" + graceful-fs@^4.1.6, graceful-fs@^4.2.0: version "4.2.6" resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.6.tgz#ff040b2b0853b23c3d31027523706f1885d76bee" @@ -2018,6 +2201,11 @@ hoist-non-react-statics@^3.3.1: dependencies: react-is "^16.7.0" +html-escaper@^2.0.0: + version "2.0.2" + resolved "https://registry.yarnpkg.com/html-escaper/-/html-escaper-2.0.2.tgz#dfd60027da36a36dfcbe236262c00a5822681453" + integrity sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg== + import-fresh@^3.2.1: version "3.3.0" resolved "https://registry.yarnpkg.com/import-fresh/-/import-fresh-3.3.0.tgz#37162c25fcb9ebaa2e6e53d5b4d88ce17d9e0c2b" @@ -2077,6 +2265,37 @@ isexe@^2.0.0: resolved "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10" integrity sha1-6PvzdNxVb/iUehDcsFctYz8s+hA= +istanbul-lib-coverage@^3.0.0, istanbul-lib-coverage@^3.2.2: + version "3.2.2" + resolved "https://registry.yarnpkg.com/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.2.tgz#2d166c4b0644d43a39f04bf6c2edd1e585f31756" + integrity sha512-O8dpsF+r0WV/8MNRKfnmrtCWhuKjxrq2w+jpzBL5UZKTi2LeVWnWOmWRxFlesJONmc+wLAGvKQZEOanko0LFTg== + +istanbul-lib-report@^3.0.0, istanbul-lib-report@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/istanbul-lib-report/-/istanbul-lib-report-3.0.1.tgz#908305bac9a5bd175ac6a74489eafd0fc2445a7d" + integrity sha512-GCfE1mtsHGOELCU8e/Z7YWzpmybrx/+dSTfLrvY8qRmaY6zXTKWn6WQIjaAFw069icm6GVMNkgu0NzI4iPZUNw== + dependencies: + istanbul-lib-coverage "^3.0.0" + make-dir "^4.0.0" + supports-color "^7.1.0" + +istanbul-lib-source-maps@^5.0.6: + version "5.0.6" + resolved "https://registry.yarnpkg.com/istanbul-lib-source-maps/-/istanbul-lib-source-maps-5.0.6.tgz#acaef948df7747c8eb5fbf1265cb980f6353a441" + integrity sha512-yg2d+Em4KizZC5niWhQaIomgf5WlL4vOOjZ5xGCmF8SnPE/mDWWXgvRExdcpCgh9lLRRa1/fSYp2ymmbJ1pI+A== + dependencies: + "@jridgewell/trace-mapping" "^0.3.23" + debug "^4.1.1" + istanbul-lib-coverage "^3.0.0" + +istanbul-reports@^3.1.7: + version "3.2.0" + resolved "https://registry.yarnpkg.com/istanbul-reports/-/istanbul-reports-3.2.0.tgz#cb4535162b5784aa623cee21a7252cf2c807ac93" + integrity sha512-HGYWWS/ehqTV3xN10i23tkPkpH46MLCIMFNCaaKNavAXTF1RkqxawEPtnjnGZ6XKSInBKkiOA5BKS+aZiY3AvA== + dependencies: + html-escaper "^2.0.0" + istanbul-lib-report "^3.0.0" + jackspeak@^3.1.2: version "3.4.3" resolved "https://registry.yarnpkg.com/jackspeak/-/jackspeak-3.4.3.tgz#8833a9d89ab4acde6188942bd1c53b6390ed5a8a" @@ -2096,11 +2315,21 @@ jotai@^2.15.0: resolved "https://registry.yarnpkg.com/jotai/-/jotai-2.15.0.tgz#2a697924b7dcfff71847e83c7493c92e3b731e88" integrity sha512-nbp/6jN2Ftxgw0VwoVnOg0m5qYM1rVcfvij+MZx99Z5IK13eGve9FJoCwGv+17JvVthTjhSmNtT5e1coJnr6aw== +js-tokens@^10.0.0: + version "10.0.0" + resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-10.0.0.tgz#dffe7599b4a8bb7fe30aff8d0235234dffb79831" + integrity sha512-lM/UBzQmfJRo9ABXbPWemivdCW8V2G8FHaHdypQaIy523snUjog0W71ayWXTjiR+ixeMyVHN2XcpnTd/liPg/Q== + "js-tokens@^3.0.0 || ^4.0.0", js-tokens@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499" integrity sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ== +js-tokens@^9.0.1: + version "9.0.1" + resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-9.0.1.tgz#2ec43964658435296f6761b34e10671c2d9527f4" + integrity sha512-mxa9E9ITFOt0ban3j6L5MpjwegGz6lBQmM1IJkWeBZGcMxto50+eWdjC/52xDbS2vy0k7vIMK0Fe2wfL9OQSpQ== + jsesc@^3.0.2: version "3.1.0" resolved "https://registry.yarnpkg.com/jsesc/-/jsesc-3.1.0.tgz#74d335a234f67ed19907fdadfac7ccf9d409825d" @@ -2204,6 +2433,22 @@ magic-string@^0.30.0, magic-string@^0.30.17: dependencies: "@jridgewell/sourcemap-codec" "^1.5.5" +magicast@^0.3.5: + version "0.3.5" + resolved "https://registry.yarnpkg.com/magicast/-/magicast-0.3.5.tgz#8301c3c7d66704a0771eb1bad74274f0ec036739" + integrity sha512-L0WhttDl+2BOsybvEOLK7fW3UA0OQ0IQ2d6Zl2x/a6vVRs3bAY0ECOSHHeL5jD+SbOpOCUEi0y1DgHEn9Qn1AQ== + dependencies: + "@babel/parser" "^7.25.4" + "@babel/types" "^7.25.4" + source-map-js "^1.2.0" + +make-dir@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/make-dir/-/make-dir-4.0.0.tgz#c3c2307a771277cd9638305f915c29ae741b614e" + integrity sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw== + dependencies: + semver "^7.5.3" + min-indent@^1.0.0, min-indent@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/min-indent/-/min-indent-1.0.1.tgz#a63f681673b30571fbe8bc25686ae746eefa9869" @@ -2216,6 +2461,13 @@ minimatch@10.0.3: dependencies: "@isaacs/brace-expansion" "^5.0.0" +minimatch@^10.2.2: + version "10.2.5" + resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-10.2.5.tgz#bd48687a0be38ed2961399105600f832095861d1" + integrity sha512-MULkVLfKGYDFYejP07QOurDLLQpcjk7Fw+7jXS2R2czRQzR56yHRveU5NDJEOviH+hETZKSkIk5c+T23GjFUMg== + dependencies: + brace-expansion "^5.0.5" + minimatch@^9.0.3, minimatch@^9.0.4: version "9.0.5" resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-9.0.5.tgz#d74f9dd6b57d83d8e98cfb82133b03978bc929e5" @@ -2243,6 +2495,11 @@ mlly@^1.7.4: pkg-types "^1.3.1" ufo "^1.6.1" +mrmime@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/mrmime/-/mrmime-2.0.1.tgz#bc3e87f7987853a54c9850eeb1f1078cd44adddc" + integrity sha512-Y3wQdFg2Va6etvQ5I82yUhGdsKrcYox6p7FfL1LbK2J4V01F9TGlepTIhnK24t7koZibmg82KGglhA1XK5IsLQ== + ms@2.1.2: version "2.1.2" resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009" @@ -2361,7 +2618,7 @@ pathval@^2.0.0: resolved "https://registry.yarnpkg.com/pathval/-/pathval-2.0.1.tgz#8855c5a2899af072d6ac05d11e46045ad0dc605d" integrity sha512-//nshmD55c46FuFw26xV/xFAaB5HF9Xdap7HJBBnrKdAd6/GxDBaNA1870O79+9ueg61cZLSVc+OaFlfmObYVQ== -picocolors@^1.1.1: +picocolors@1.1.1, picocolors@^1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/picocolors/-/picocolors-1.1.1.tgz#3d321af3eab939b083c8f929a1d12cda81c26b6b" integrity sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA== @@ -2371,6 +2628,11 @@ picomatch@^4.0.2, picomatch@^4.0.3: resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-4.0.3.tgz#796c76136d1eead715db1e7bad785dedd695a042" integrity sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q== +picomatch@^4.0.4: + version "4.0.4" + resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-4.0.4.tgz#fd6f5e00a143086e074dffe4c924b8fb293b0589" + integrity sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A== + pkg-types@^1.3.1: version "1.3.1" resolved "https://registry.yarnpkg.com/pkg-types/-/pkg-types-1.3.1.tgz#bd7cc70881192777eef5326c19deb46e890917df" @@ -2389,6 +2651,20 @@ pkg-types@^2.3.0: exsolve "^1.0.7" pathe "^2.0.3" +playwright-core@1.60.0: + version "1.60.0" + resolved "https://registry.yarnpkg.com/playwright-core/-/playwright-core-1.60.0.tgz#24e0d9cc4730713db5dffcace29b5e4696b1907a" + integrity sha512-9bW6zvX/m0lEbgTKJ6YppOKx8H3VOPBMOCFh2irXFOT4BbHgrx5hPjwJYLT40Lu+4qtD36qKc/Hn56StUW57IA== + +playwright@^1: + version "1.60.0" + resolved "https://registry.yarnpkg.com/playwright/-/playwright-1.60.0.tgz#89710863a51f21112633ef8b6b182594d3bfd7b5" + integrity sha512-hheHdokM8cdqCb0lcE3s+zT4t4W+vvjpGxsZlDnikarzx8tSzMebh3UiFtgqwFwnTnjYQcsyMF8ei2mCO/tpeA== + dependencies: + playwright-core "1.60.0" + optionalDependencies: + fsevents "2.3.2" + postcss@^8.5.6: version "8.5.6" resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.5.6.tgz#2825006615a619b4f62a9e7426cc120b349a8f3c" @@ -2595,6 +2871,11 @@ semver@^6.3.1: resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.1.tgz#556d2ef8689146e46dcea4bfdd095f3434dffcb4" integrity sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA== +semver@^7.5.3: + version "7.8.1" + resolved "https://registry.yarnpkg.com/semver/-/semver-7.8.1.tgz#bf4970b5e70fda0686363cc18bfe8805d5ed957e" + integrity sha512-rkVq3IXh+4FDGch+KwzX3aV9W3kO54GyEgpvBzSyctDA6Xtd7RJQV1xmXbeQp5v7+VzLOfVqiutSE6GICgPFvg== + semver@^7.6.2: version "7.7.2" resolved "https://registry.yarnpkg.com/semver/-/semver-7.7.2.tgz#67d99fdcd35cec21e6f8b87a7fd515a33f982b58" @@ -2619,12 +2900,26 @@ shebang-regex@^3.0.0: resolved "https://registry.yarnpkg.com/shebang-regex/-/shebang-regex-3.0.0.tgz#ae16f1644d873ecad843b0307b143362d4c42172" integrity sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A== +siginfo@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/siginfo/-/siginfo-2.0.0.tgz#32e76c70b79724e3bb567cb9d543eb858ccfaf30" + integrity sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g== + signal-exit@^4.0.1: version "4.1.0" resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-4.1.0.tgz#952188c1cbd546070e2dd20d0f41c0ae0530cb04" integrity sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw== -source-map-js@^1.2.1: +sirv@^3.0.1: + version "3.0.2" + resolved "https://registry.yarnpkg.com/sirv/-/sirv-3.0.2.tgz#f775fccf10e22a40832684848d636346f41cd970" + integrity sha512-2wcC/oGxHis/BoHkkPwldgiPSYcpZK3JU28WoMVv55yHJgcZ8rlXvuG9iZggz+sU1d4bRgIGASwyWqjxu3FM0g== + dependencies: + "@polka/url" "^1.0.0-next.24" + mrmime "^2.0.0" + totalist "^3.0.0" + +source-map-js@^1.2.0, source-map-js@^1.2.1: version "1.2.1" resolved "https://registry.yarnpkg.com/source-map-js/-/source-map-js-1.2.1.tgz#1ce5650fddd87abc099eda37dcff024c2667ae46" integrity sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA== @@ -2644,6 +2939,16 @@ sprintf-js@~1.0.2: resolved "https://registry.yarnpkg.com/sprintf-js/-/sprintf-js-1.0.3.tgz#04e6926f662895354f3dd015203633b857297e2c" integrity sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw= +stackback@0.0.2: + version "0.0.2" + resolved "https://registry.yarnpkg.com/stackback/-/stackback-0.0.2.tgz#1ac8a0d9483848d1695e418b6d031a3c3ce68e3b" + integrity sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw== + +std-env@^3.9.0: + version "3.10.0" + resolved "https://registry.yarnpkg.com/std-env/-/std-env-3.10.0.tgz#d810b27e3a073047b2b5e40034881f5ea6f9c83b" + integrity sha512-5GS12FdOZNliM5mAOxFRg7Ir0pWz8MdpYm6AY6VPkGpbA7ZzmbzNcBJQ0GPvvyWgcY7QAhCgf9Uy89I03faLkg== + storybook@^9.1.10: version "9.1.10" resolved "https://registry.yarnpkg.com/storybook/-/storybook-9.1.10.tgz#c06cfd8b77522cf17bd22d82d1e83af96362a78e" @@ -2739,6 +3044,13 @@ strip-json-comments@~3.1.1: resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-3.1.1.tgz#31f1281b3832630434831c310c01cccda8cbe006" integrity sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig== +strip-literal@^3.0.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/strip-literal/-/strip-literal-3.1.0.tgz#222b243dd2d49c0bcd0de8906adbd84177196032" + integrity sha512-8r3mkIM/2+PpjHoOtiAW8Rg3jJLHaV7xPwG+YRGrv6FP0wwk/toTpATxWYOW0BKdWwl82VT2tFYi5DlROa0Mxg== + dependencies: + js-tokens "^9.0.1" + stylis@4.2.0: version "4.2.0" resolved "https://registry.yarnpkg.com/stylis/-/stylis-4.2.0.tgz#79daee0208964c8fe695a42fcffcac633a211a51" @@ -2770,11 +3082,38 @@ supports-preserve-symlinks-flag@^1.0.0: resolved "https://registry.yarnpkg.com/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz#6eda4bd344a3c94aea376d4cc31bc77311039e09" integrity sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w== +test-exclude@^7.0.1: + version "7.0.2" + resolved "https://registry.yarnpkg.com/test-exclude/-/test-exclude-7.0.2.tgz#482392077630bc57d5630c13abe908bb910dfc65" + integrity sha512-u9E6A+ZDYdp7a4WnarkXPZOx8Ilz46+kby6p1yZ8zsGTz9gYa6FIS7lj2oezzNKmtdyyJNNmmXDppga5GB7kSw== + dependencies: + "@istanbuljs/schema" "^0.1.2" + glob "^10.4.1" + minimatch "^10.2.2" + tiny-invariant@^1.3.3: version "1.3.3" resolved "https://registry.yarnpkg.com/tiny-invariant/-/tiny-invariant-1.3.3.tgz#46680b7a873a0d5d10005995eb90a70d74d60127" integrity sha512-+FbBPE1o9QAYvviau/qC5SE3caw21q3xkvWKBtja5vgqOWIHHJ3ioaq1VPfn/Szqctz2bU/oYeKd9/z5BL+PVg== +tinybench@^2.9.0: + version "2.9.0" + resolved "https://registry.yarnpkg.com/tinybench/-/tinybench-2.9.0.tgz#103c9f8ba6d7237a47ab6dd1dcff77251863426b" + integrity sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg== + +tinyexec@^0.3.2: + version "0.3.2" + resolved "https://registry.yarnpkg.com/tinyexec/-/tinyexec-0.3.2.tgz#941794e657a85e496577995c6eef66f53f42b3d2" + integrity sha512-KQQR9yN7R5+OSwaK0XQoj22pwHoTlgYqmUscPYoknOoWCWfj/5/ABTMRi69FrKU5ffPVh5QcFikpWJI/P1ocHA== + +tinyglobby@^0.2.14: + version "0.2.16" + resolved "https://registry.yarnpkg.com/tinyglobby/-/tinyglobby-0.2.16.tgz#1c3b7eb953fce42b226bc5a1ee06428281aff3d6" + integrity sha512-pn99VhoACYR8nFHhxqix+uvsbXineAasWm5ojXoN8xEwK5Kd3/TrhNn1wByuD52UxWRLy8pu+kRMniEi6Eq9Zg== + dependencies: + fdir "^6.5.0" + picomatch "^4.0.4" + tinyglobby@^0.2.15: version "0.2.15" resolved "https://registry.yarnpkg.com/tinyglobby/-/tinyglobby-0.2.15.tgz#e228dd1e638cea993d2fdb4fcd2d4602a79951c2" @@ -2783,6 +3122,11 @@ tinyglobby@^0.2.15: fdir "^6.5.0" picomatch "^4.0.3" +tinypool@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/tinypool/-/tinypool-1.1.1.tgz#059f2d042bd37567fbc017d3d426bdd2a2612591" + integrity sha512-Zba82s87IFq9A9XmjiX5uZA/ARWDrB03OHlq+Vw1fSdt0I+4/Kutwy8BP4Y/y/aORMo61FQ0vIb5j44vSo5Pkg== + tinyrainbow@^1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/tinyrainbow/-/tinyrainbow-1.2.0.tgz#5c57d2fc0fb3d1afd78465c33ca885d04f02abb5" @@ -2803,6 +3147,11 @@ tinyspy@^4.0.3: resolved "https://registry.yarnpkg.com/tinyspy/-/tinyspy-4.0.4.tgz#d77a002fb53a88aa1429b419c1c92492e0c81f78" integrity sha512-azl+t0z7pw/z958Gy9svOTuzqIk6xq+NSheJzn5MMWtWTFywIacg2wUlzKFGtt3cthx0r2SxMK0yzJOR0IES7Q== +totalist@^3.0.0: + version "3.0.1" + resolved "https://registry.yarnpkg.com/totalist/-/totalist-3.0.1.tgz#ba3a3d600c915b1a97872348f79c127475f6acf8" + integrity sha512-sf4i37nQ2LBx4m3wB74y+ubopq6W/dIzXg0FDGjsYnZHVa1Da8FH853wlL2gtUhg+xJXjfk3kUZS3BRoQeoQBQ== + ts-dedent@^2.0.0: version "2.2.0" resolved "https://registry.yarnpkg.com/ts-dedent/-/ts-dedent-2.2.0.tgz#39e4bd297cd036292ae2394eb3412be63f563bb5" @@ -2875,6 +3224,17 @@ uri-js@^4.2.2, uri-js@^4.4.1: dependencies: punycode "^2.1.0" +vite-node@3.2.4: + version "3.2.4" + resolved "https://registry.yarnpkg.com/vite-node/-/vite-node-3.2.4.tgz#f3676d94c4af1e76898c162c92728bca65f7bb07" + integrity sha512-EbKSKh+bh1E1IFxeO0pg1n4dvoOTt0UDiXMd/qn++r98+jPO1xtJilvXldeuQ8giIB5IkpjCgMleHMNEsGH6pg== + 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" + vite-plugin-dts@^4.5.4: version "4.5.4" resolved "https://registry.yarnpkg.com/vite-plugin-dts/-/vite-plugin-dts-4.5.4.tgz#51b60aaaa760d9cf5c2bb3676c69d81910d6b08c" @@ -2890,7 +3250,7 @@ vite-plugin-dts@^4.5.4: local-pkg "^1.0.0" magic-string "^0.30.17" -vite@^7.1.9: +vite@7.1.9, "vite@^5.0.0 || ^6.0.0 || ^7.0.0-0", vite@^7.1.9: version "7.1.9" resolved "https://registry.yarnpkg.com/vite/-/vite-7.1.9.tgz#ba844410e5d0c0f2a4eaf17a52af60ebea322cbf" integrity sha512-4nVGliEpxmhCL8DslSAUdxlB6+SMrhB0a1v5ijlh1xB1nEPuy1mxaHxysVucLHuWryAxLWg6a5ei+U4TLn/rFg== @@ -2904,6 +3264,40 @@ vite@^7.1.9: optionalDependencies: fsevents "~2.3.3" +vitest-browser-react@^1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/vitest-browser-react/-/vitest-browser-react-1.0.1.tgz#e36f0a9ca46bf622841f0285defe0bebbb37e16d" + integrity sha512-LqiGFCdknrbMoSDWXTCTrPsED3SvdIXIgYOOZyYUNj2dkJusW2eF6NENOlBlxwq+FBQqzNK1X59b+b03pXFpAQ== + +vitest@^3: + version "3.2.4" + resolved "https://registry.yarnpkg.com/vitest/-/vitest-3.2.4.tgz#0637b903ad79d1539a25bc34c0ed54b5c67702ea" + integrity sha512-LUCP5ev3GURDysTWiP47wRRUpLKMOfPh+yKTx3kVIEiu5KOMeqzpnYNsKyOoVrULivR8tLcks4+lga33Whn90A== + 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" + vscode-uri@^3.0.8: version "3.1.0" resolved "https://registry.yarnpkg.com/vscode-uri/-/vscode-uri-3.1.0.tgz#dd09ec5a66a38b5c3fffc774015713496d14e09c" @@ -2921,6 +3315,14 @@ which@^2.0.1: dependencies: isexe "^2.0.0" +why-is-node-running@^2.3.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/why-is-node-running/-/why-is-node-running-2.3.0.tgz#a3f69a97107f494b3cdc3bdddd883a7d65cebf04" + integrity sha512-hUrmaWBdVDcxvYqnyh09zunKzROWjbZTiNy8dBEjkS7ehEDQibXJ7XvlmtbwuTclUiIyN+CyXQD4Vmko8fNm8w== + dependencies: + siginfo "^2.0.0" + stackback "0.0.2" + "wrap-ansi-cjs@npm:wrap-ansi@^7.0.0": version "7.0.0" resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43" @@ -2944,6 +3346,11 @@ ws@^8.18.0: resolved "https://registry.yarnpkg.com/ws/-/ws-8.18.3.tgz#b56b88abffde62791c639170400c93dcb0c95472" integrity sha512-PEIGCY5tSlUt50cqyMXfCzX+oOPqN0vuGqWzbcJ2xvnkzkq46oOpz7dQaTDBdfICb4N14+GARUDw2XV2N4tvzg== +ws@^8.18.2: + version "8.21.0" + resolved "https://registry.yarnpkg.com/ws/-/ws-8.21.0.tgz#012e413fc07429945121b0c153158c4343086951" + integrity sha512-Vsp28b7DRcimFQvrqu2Wek3z1iYxDCWqHYB8Qsnk/S4RfaCQzPGPyBNuVjJV3cd6UiKtUtp6sNM77gWvzcCH+g== + yallist@^3.0.2: version "3.1.1" resolved "https://registry.yarnpkg.com/yallist/-/yallist-3.1.1.tgz#dbb7daf9bfd8bac9ab45ebf602b8cbad0d5d08fd"