diff --git a/.browserslistrc b/.browserslistrc new file mode 100644 index 00000000..7f723f31 --- /dev/null +++ b/.browserslistrc @@ -0,0 +1,13 @@ +# Fixed ES2022 browser baseline for Wouter 4; see specs/browser-support.md. +# Packages ship source. This is a consumer/tooling target, not a build step. +Chrome >= 94 +and_chr >= 94 +Edge >= 94 +Firefox >= 93 +and_ff >= 93 +Safari >= 16.4 +ios_saf >= 16.4 +Opera >= 80 +op_mob >= 66 +Samsung >= 17 +Android >= 94 diff --git a/.cursor/rules/contributing.mdc b/.cursor/rules/contributing.mdc deleted file mode 100644 index 5c637e65..00000000 --- a/.cursor/rules/contributing.mdc +++ /dev/null @@ -1,242 +0,0 @@ ---- -description: -globs: -alwaysApply: true ---- - -# Wouter - Minimalist React Router - -## Project Overview - -Wouter is a **minimalist-friendly ~2.1KB React router** that focuses on performance, small bundle size, and simplicity. It provides hook-based routing for React and Preact applications. - -### Key Features - -- Tiny bundle size (2.1KB gzipped vs 18.7KB React Router) -- Hook-based API (`useLocation`, `useRoute`, `useParams`, etc.) -- Zero dependencies -- Supports React and Preact -- Optional top-level `` component -- Nested routing support -- SSR support -- Memory location for testing - -## Project Structure - -``` -wouter/ -├── packages/ -│ ├── wouter/ # Main React package -│ │ ├── src/ -│ │ │ ├── index.js # Main router implementation -│ │ │ ├── use-browser-location.js -│ │ │ ├── use-hash-location.js -│ │ │ ├── memory-location.js -│ │ │ ├── react-deps.js # React imports -│ │ │ └── paths.js # Path utilities -│ │ ├── test/ # Test files -│ │ ├── types/ # TypeScript definitions -│ │ └── esm/ # Built ES modules -│ └── wouter-preact/ # Preact package -├── assets/ # Documentation assets -└── .github/ # GitHub workflows -``` - -## Development Workflow - -### Prerequisites - -- Node.js v20.17.0+ or v22.9.0+ -- npm - -### Commands - -```bash -# Install dependencies -npm install - -# Run tests (watch mode) -npm test - -# Run tests (single run) -npm test -- --run - -# Run specific test file -npm test -- packages/wouter/test/router.test.tsx --run - -# Run tests with coverage -npm test -- --coverage - -# Build packages -npm run build - -# Type checking -npm run typecheck - -# Lint code -npm run lint -``` - -### Test Files Organization - -- `test/router.test.tsx` - Router component and memoization tests -- `test/use-location.test.tsx` - Location hook tests -- `test/use-route.test.tsx` - Route matching tests -- `test/link.test.tsx` - Link component tests -- `test/memory-location.test.ts` - Memory location for testing -- `test/nested-route.test.tsx` - Nested routing tests -- `test/ssr.test.tsx` - Server-side rendering tests - -## Key Development Principles - -### Performance First - -- **Bundle size matters**: Every byte counts. Use size-limit CI checks -- **Stable object references**: Prevent unnecessary re-renders with proper memoization -- **Minimize dependencies**: Zero external dependencies policy - -### Code Style - -- **Functional programming**: Prefer hooks over class components -- **Compact code**: Optimize for bundle size (may sacrifice readability) -- **React patterns**: Use `React.memo`, `useCallback`, `useMemo` appropriately - -### Testing Guidelines - -- **Comprehensive coverage**: Test all public APIs -- **Real-world scenarios**: Include complex use cases (like language switching) -- **Memory location**: Use for isolated testing -- **Memoization tests**: Verify stable object references - -## Core APIs - -### Hooks - -- `useLocation()` - Get/set current location -- `useRoute(pattern)` - Match current location against pattern -- `useParams()` - Extract route parameters -- `useSearch()` - Get search/query string -- `useSearchParams()` - Get/set URLSearchParams -- `useRouter()` - Access router configuration - -### Components - -- `` - Optional configuration wrapper -- `` - Conditional rendering based on path -- `` - Navigation component -- `` - Exclusive routing -- `` - Programmatic navigation - -### Location Hooks - -- `useBrowserLocation` - Browser history API -- `useHashLocation` - Hash-based routing -- `memoryLocation` - In-memory for testing - -## Performance Considerations - -### Router Memoization - -The Router component uses sophisticated memoization to prevent unnecessary re-renders: - -- Only creates new router objects when props actually change -- Preserves stable references for `React.memo` components -- Critical for performance in complex applications - -### Bundle Optimization - -- Use `// ... existing code ...` pattern in edits to minimize diffs -- Prefer shorter variable names in production builds -- Optimize for gzip/brotli compression - -## Common Patterns - -### Testing Router Components - -```javascript -import { memoryLocation } from "wouter/memory-location"; - -const { hook } = memoryLocation({ path: "/test/path" }); -render( - - - -); -``` - -### Custom Location Hooks - -```javascript -const useCustomLocation = () => { - const [location, setLocation] = useBrowserLocation(); - // Add custom logic here - return [location, setLocation]; -}; -``` - -### Nested Routing - -```javascript - - - - - -``` - -## Debugging Tips - -### Common Issues - -1. **Router memoization**: Check if unnecessary re-renders occur -2. **Base path conflicts**: Verify base path inheritance in nested routers -3. **Pattern matching**: Use regexparam syntax for route patterns -4. **SSR hydration**: Ensure client/server router configs match - -### Debugging Tools - -- React DevTools Profiler for render tracking -- Network tab for bundle size verification -- Console warnings for deprecated patterns - -## Contributing Guidelines - -### Before Making Changes - -1. Run full test suite: `npm test -- --run` -2. Check bundle size impact -3. Verify TypeScript definitions -4. Test with both React and Preact - -### Adding New Features - -1. Consider bundle size impact -2. Add comprehensive tests -3. Update TypeScript definitions -4. Document in README if public API -5. Maintain backward compatibility - -### Performance Changes - -1. Add before/after performance tests -2. Verify memoization behavior -3. Test with complex nested scenarios -4. Check for memory leaks in long-running apps - -## Architecture Notes - -### Router Context System - -- Default router created on-demand -- Context inheritance with override capabilities -- Memoization prevents cascade re-renders -- Base path accumulation in nested routers - -### Pattern Matching - -- Uses regexparam library internally -- Supports named parameters, wildcards, optional segments -- Regex patterns supported for complex matching -- Loose mode for nested routing - -This project prioritizes performance and minimalism while maintaining full feature parity with larger routing solutions. diff --git a/.cursor/rules/publishing.mdc b/.cursor/rules/publishing.mdc deleted file mode 100644 index dc89c544..00000000 --- a/.cursor/rules/publishing.mdc +++ /dev/null @@ -1,109 +0,0 @@ ---- -description: -globs: -alwaysApply: true ---- -# Publishing Wouter Packages - -This document outlines the process for publishing new versions of the wouter packages to npm. - -## Prerequisites - -- Ensure you have npm publish permissions for both `wouter` and `wouter-preact` packages -- Have npm authentication set up (you'll need OTP access) -- All tests should be passing -- All changes should be committed to the repository - -## Publishing Process - -### 1. Version Bump - -Update the version in both package.json files: -- `packages/wouter/package.json` -- `packages/wouter-preact/package.json` - -For semantic versioning: -- **Patch** (x.x.X): Bug fixes, small improvements -- **Minor** (x.X.x): New features, backward compatible -- **Major** (X.x.x): Breaking changes - -### 2. Dry Run Validation - -Run dry publish commands to validate the packages before actual publishing: - -```bash -npm publish --dry-run -w wouter -npm publish --dry-run -w wouter-preact -``` - -This will show you: -- Package contents and file list -- Bundle size information -- Version confirmation -- Any potential issues - -### 3. Build Verification (Optional) - -Check the build artifacts in the `/esm/` folders to ensure the latest updates are properly built: -- `packages/wouter/esm/` -- `packages/wouter-preact/esm/` - -The `prepublishOnly` script automatically builds the packages, but it's good to verify. - -## ⚠️ Confirmation Required - -**ALWAYS ask for user confirmation before proceeding with the following steps:** - -### 4. Publish to npm - -Publish both packages to the npm registry: - -```bash -npm publish -w wouter -npm publish -w wouter-preact -``` - -**Note:** npm might fail with "EOTP" (Error One-Time Password) even when the OTP was entered correctly in the browser. This is a known npm issue - the packages are usually still published successfully despite the error message. - -### 5. Git Commit and Tag - -After successful publishing: - -1. **Commit the version changes:** - ```bash - git add packages/wouter/package.json packages/wouter-preact/package.json - git commit -m "Bump version to X.X.X" - ``` - -2. **Create a version tag:** - ```bash - git tag vX.X.X - ``` - -3. **Push to remote (optional):** - ```bash - git push origin main - git push origin vX.X.X - ``` - -## Troubleshooting - -### Common Issues - -- **Authentication errors**: Run `npm login` or use the browser authentication flow -- **OTP timeout**: Generate a fresh OTP code from your authenticator app -- **Permission denied**: Ensure you have publish permissions for both packages -- **Version conflicts**: Check if the version already exists on npm - -### Verification - -After publishing, verify the packages are available: -- Check [wouter on npm](mdc:https:/www.npmjs.com/package/wouter) -- Check [wouter-preact on npm](mdc:https:/www.npmjs.com/package/wouter-preact) -- Test installation: `npm install wouter@latest` - -## Package Information - -- **wouter**: Main React package (~22KB package size) -- **wouter-preact**: Preact-specific package (~22KB package size) -- Both packages maintain version parity and should be published together \ No newline at end of file diff --git a/.github/workflows/ci-tests.yml b/.github/workflows/ci-tests.yml index b727ae64..591b292d 100644 --- a/.github/workflows/ci-tests.yml +++ b/.github/workflows/ci-tests.yml @@ -25,7 +25,7 @@ jobs: run: bun install --frozen-lockfile - name: Prepare wouter-preact (copy source files) - run: cd packages/wouter-preact && npm run prepublishOnly + run: bun run --cwd packages/wouter-preact prepublishOnly - name: Run test run: bun test --coverage --coverage-reporter=lcov --coverage-reporter=text @@ -40,3 +40,39 @@ jobs: uses: coverallsapp/github-action@v2 with: file: ./coverage/lcov.info + + react-compatibility: + runs-on: ubuntu-latest + strategy: + matrix: + react: ["18.2.0", "18.3.1", "19.0.0"] + steps: + - uses: actions/checkout@v4 + - uses: oven-sh/setup-bun@v2 + with: + bun-version: latest + - run: bun install --frozen-lockfile + - name: Install the React version under test + run: bun add --dev --exact react@${{ matrix.react }} react-dom@${{ matrix.react }} + - name: Test supported React versions + run: bun test packages/wouter/test + + typescript-consumers: + runs-on: ubuntu-latest + strategy: + matrix: + typescript: ["5.2.2", "workspace"] + react-types: ["18", "19"] + steps: + - uses: actions/checkout@v4 + - uses: oven-sh/setup-bun@v2 + with: + bun-version: latest + - run: bun install --frozen-lockfile + - name: Install React declarations under test + run: bun add --dev @types/react@${{ matrix.react-types }} @types/react-dom@${{ matrix.react-types }} + - name: Install the minimum supported TypeScript version + if: matrix.typescript != 'workspace' + run: bun add --dev --exact typescript@${{ matrix.typescript }} + - name: Check strict React and Preact consumers + run: bun run test-types:consumer diff --git a/AGENTS.md b/AGENTS.md index b8100b77..2b6d7d2d 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -1,111 +1,140 @@ ---- -description: Use Bun instead of Node.js, npm, pnpm, or vite. -globs: "*.ts, *.tsx, *.html, *.css, *.js, *.jsx, package.json" -alwaysApply: false ---- +# Working on Wouter + +This is the shared project guide for coding agents. Keep it +focused on durable conventions; use the source, package scripts, and CI workflows +for details that change frequently. + +## Project and layout + +Wouter is a small, hook-based router for React and Preact. Prioritize a small +bundle, few dependencies, stable references, and predictable routing behavior. +`regexparam` is a runtime dependency; this is not a zero-dependency project. + +- `packages/wouter/src/`: canonical JavaScript implementation, shipped directly + as ES modules. The root `build` script is a no-op; there is no required `esm/` + build output. +- `packages/wouter-preact/`: shares Wouter's runtime sources except for its own + `src/react-deps.js` adapter. Shared files are copied by `prepublishOnly` and + ignored by Git. Edit the canonical sources instead of the generated copies. +- `packages/*/types/`: hand-maintained TypeScript declarations. Keep React and + Preact declarations aligned while preserving framework-specific types. Wouter's + `src/*.d.ts` files forward to these declarations for source imports. +- `packages/*/test/`: runtime tests and declaration tests (`*.test-d.ts[x]`). +- `packages/magazin/`: private storefront demo using Bun, React, SSR, and Tailwind. +- `README.md`: canonical public documentation; package READMEs are copied from it. + [specs/browser-support.md](specs/browser-support.md) records the Wouter 4 + compatibility decisions and their rationale. + +## Tooling and checks + +Use Bun for installation, scripts, tests, and builds. Keep the single root +`bun.lock`; do not introduce nested lockfiles or a second package manager. +Prefer Bun's built-in APIs (`Bun.file`, `Bun.serve`, `Bun.build`, `Bun.$`) for +new tooling. Bun loads `.env` automatically. Demo frontend work uses Bun's HTML +entrypoints rather than Vite. + +Run commands from the repository root unless specified otherwise: -Default to using Bun instead of Node.js. - -- Use `bun ` instead of `node ` or `ts-node ` -- Use `bun test` instead of `jest` or `vitest` -- Use `bun build ` instead of `webpack` or `esbuild` -- Use `bun install` instead of `npm install` or `yarn install` or `pnpm install` -- Use `bun run - - +```sh +bun install # CI uses --frozen-lockfile +bun test # React, Preact, and demo runtime tests +bun test --watch +bun test packages/wouter/test/router.test.tsx +bun test --coverage +bun run test-types # library code and declaration tests +bun run test-types:consumer # strict public API checks, three resolution modes +bun run lint ``` -With the following `frontend.tsx`: - -```tsx#frontend.tsx -import React from "react"; +The root `bunfig.toml` preloads Happy DOM, jest-dom matchers, and cleanup. Follow +the existing `bun:test` and Testing Library patterns; use `memoryLocation` for +isolated routing tests. CI requires 99% line coverage for the libraries. Demo +code and generated Preact source copies are excluded from that coverage metric. -// import .css files directly and it works -import './index.css'; +Preact tests create and then delete the shared source copies. Refresh them +**after tests**, before size checks or packaging; do not run these operations +concurrently with the tests: -import { createRoot } from "react-dom/client"; - -const root = createRoot(document.body); - -export default function Frontend() { - return

Hello, world!

; -} - -root.render(); +```sh +bun run --cwd packages/wouter-preact prepublishOnly +bun run size +bun run size:dependencies ``` -Then, run index.ts +`size` enforces the budgets in `package.json`. `size:dependencies` reports raw, +gzip, and Brotli sizes including non-peer dependencies; these are different +measurements. Compare equivalent sources with the same tooling and dependencies. +Do not raise budgets just to make a regression pass. + +Keep changes compact and consistent with nearby code: two spaces, double quotes, +and semicolons. Format changed files with the installed Prettier. The `fix:p` +script rewrites JavaScript and TypeScript across the repository, so use it only +when that scope is intended. Run checks appropriate to the change; public API +changes need runtime coverage, declarations for both frameworks, and README +updates. Documentation-only changes do not require the full runtime suite. + +## Compatibility and routing constraints + +Current development prepares Wouter 4: React **18.2+**, Preact **10.x**, and +TypeScript **5.2+**. Published source targets **ES2022** with fixed browser floors +in `.browserslistrc`. Keep published modules synchronously initialized, without +top-level await. There is no transpilation or automatic polyfill step. + +- Preserve stable router, parameter, and navigation callback references. Parameter + caching must distinguish missing keys from keys whose value is `undefined`. +- Keep the React 18 store snapshot workaround and the small Preact adapter unless + compatibility tests justify changing them. `useEffectEvent` cannot replace + callbacks used by click/navigation handlers. Keep React 18 `forwardRef` support. +- Routing changes should cover nested/base paths, absolute `~/` paths, search + updates, and SSR/hydration. Preserve explicit empty search values and browser-free + SSR imports. Keep History API patching idempotent and subscriber notifications + grouped so parent and child routing updates stay consistent. +- `aroundNav` wraps navigation through `useLocation`, `Link`, and `Redirect`. + Browser Back/Forward, direct History API calls, and low-level `navigate` bypass + it. View transitions remain an application integration; do not add `react-dom` + to the library for them. +- Preserve strict consumer checks under bundler, NodeNext, and legacy node + resolution, including optional properties and unchecked indexed access. + Custom hook types require explicit type arguments; child components cannot + infer them automatically from router context. + +## Magazin demo + +Run `bun run --cwd packages/magazin dev` (port 3002 by default; `PORT` overrides +it). Use `bun run --cwd packages/magazin prod` for production mode. The root +library type check excludes the demo; check it separately with +`bun x --no-install tsc -p packages/magazin/tsconfig.json --noEmit`. + +Preserve the recent demo fixes: cart actions update shared state, counts and +totals include quantities, missing products set HTTP 404 through `ssrContext`, +and navigation/filter/sort controls expose accessible names and state. Display +the Wouter version from package metadata. Regression tests live alongside the +demo; its React/Helmet requirements do not set the library's compatibility floor. + +## Releases + +Keep `wouter` and `wouter-preact` versions in sync, using semantic versioning. +Read their manifests for the current version and exports. Both publish `src/` +and declaration files; Magazin is private and must not be published. + +Before a release, run the relevant tests, type checks, lint, and size checks. +Prepare both packages and inspect the dry-run contents, versions, and exports: ```sh -bun --hot ./index.ts +bun run --cwd packages/wouter prepublishOnly +bun run --cwd packages/wouter-preact prepublishOnly +bun publish --cwd packages/wouter --dry-run +bun publish --cwd packages/wouter-preact --dry-run ``` -For more information, read the Bun API docs in `node_modules/bun-types/docs/**.md`. +Publishing, release commits/tags, and pushes require explicit user authorization. +If it is not already given, finish preparation and ask before those steps. Use +the intended registry dist-tag explicitly (`next` for the current prerelease +series); do not accidentally publish a prerelease under `latest`. + +Publish both packages with the same version and dist-tag, using registry +credentials with access to both. Verify each registry version and tag afterward. +If authentication or OTP reports an ambiguous failure, check registry state +before retrying; an error alone does not prove success or failure. Create the +`vX.X.X` release tag and push the intended branch/tag within the authorized scope; +do not assume the release branch is `main`. diff --git a/README.md b/README.md index 16ebc563..fd580959 100644 --- a/README.md +++ b/README.md @@ -21,8 +21,7 @@ by Katya Simacheva -- Minimum dependencies, only **2.1 KB** gzipped vs 18.7KB - [React Router](https://github.com/ReactTraining/react-router). +- Minimum dependencies, about **2.4 kB** minified and gzipped (React excluded). - Supports both **React** and **[Preact](https://preactjs.com/)**! Read _["Preact support" section](#preact-support)_ for more details. - No top-level `` component, it is **fully optional**. @@ -135,7 +134,11 @@ const App = () => ( ### Browser Support -This library is designed for **ES2020+** compatibility. If you need to support older browsers, make sure that you transpile `node_modules`. Additionally, the minimum supported TypeScript version is 4.1 in order to support route parameter inference. +Wouter targets modern browsers with **ES2022 support** and native URL, History, +and DOM event APIs. Packages ship as ES modules without transpilation or polyfills. + +Requires **React 18.2+** or **Preact 10.x** with `wouter-preact`. +TypeScript projects require **TypeScript 5.2+** and matching framework types. ## Wouter API @@ -814,8 +817,48 @@ It's the same function that is used internally. ### Can I use _wouter_ in my TypeScript project? -Yes! Although the project isn't written in TypeScript, the type definition files are bundled with -the package. +Yes! Both packages include declarations for TypeScript 5.2+. Route literals infer +required and optional parameters, filename suffixes, and wildcard captures: + +```tsx +import { useRoute } from "wouter"; + +const [matches, params] = useRoute("/files/:name.txt/*?"); +if (matches) { + params.name; // string + params["*"]; // string | undefined +} +``` + +You can also provide a parameter interface to `useRoute`, `useParams`, or `Route`. +This is an assertion about your route or custom parser, not runtime validation. +For paths only known at runtime, named parameter lookups return `string | undefined`. + +Custom location hooks retain their navigation options and state types when supplied +as the hook's type argument. For example, inside a router using this memory hook: + +```tsx +import { useSearchParams } from "wouter"; +import { memoryLocation } from "wouter/memory-location"; + +const memory = memoryLocation<{ from: string }>(); + +function SearchControls() { + const [, setSearch] = useSearchParams(); + return ( + + ); +} +``` + +Use the same type argument with `useLocation`, `Link`, and `Redirect`. The router +context does not automatically infer a custom hook's types in child components. +The declarations are checked with bundler, NodeNext, and legacy node resolution, +including `exactOptionalPropertyTypes` and `noUncheckedIndexedAccess`. ### How can add animated route transitions? @@ -1094,7 +1137,15 @@ Wouter's motto is **"Minimalist-friendly"**. - `wouter-preact` reuses the same source except for `react-deps.js` (Preact-specific hooks) - Type definitions are duplicated between packages (not ideal, but works for now) -**Development:** Tests run directly from source files (no build required). Run `npm run test` for interactive mode or `npm run test -- --run` for a single run. Use `npm run build` to build the distributable package before publishing. +**Development:** Run `bun install`, then `bun test` (or `bun test --watch`), +`bun run test-types`, and `bun run lint`. Type tests live alongside runtime tests +in `packages/*/test/*.test-d.ts` and `*.test-d.tsx`. Run +`bun run test-types:consumer` for the stricter public API checks under all three +module resolution modes. Tests and published packages use the +source files directly; no compilation step is required. After tests, run +`bun run --cwd packages/wouter-preact prepublishOnly` to refresh Preact's shared +sources before `bun run size` or packaging. `bun run size:dependencies` reports +raw, gzip, and Brotli sizes including non-peer dependencies. ## Acknowledgements diff --git a/bun.lock b/bun.lock index 868be107..0918497c 100644 --- a/bun.lock +++ b/bun.lock @@ -14,7 +14,7 @@ "@types/react": "^19", "@types/react-dom": "^19", "copyfiles": "^2.4.1", - "eslint": "^7.19.0", + "eslint": "^8.57.1", "eslint-plugin-react-hooks": "^4.6.2", "happy-dom": "^20.0.10", "husky": "^4.3.0", @@ -45,18 +45,17 @@ }, "packages/wouter": { "name": "wouter", - "version": "3.11.0", + "version": "4.0.0-next.0", "dependencies": { "regexparam": "^3.0.0", - "use-sync-external-store": "^1.0.0", }, "peerDependencies": { - "react": ">=16.8.0", + "react": ">=18.2.0", }, }, "packages/wouter-preact": { "name": "wouter-preact", - "version": "3.11.0", + "version": "4.0.0-next.0", "dependencies": { "regexparam": "^3.0.0", }, @@ -75,8 +74,6 @@ "@babel/helper-validator-identifier": ["@babel/helper-validator-identifier@7.27.1", "", {}, "sha512-D2hP9eA+Sqx1kBZgzxZh0y1trbuU+JoDkiEwqhQ36nodYqJwyEIhPSdMNd7lOm/4io72luTPWH20Yda0xOuUow=="], - "@babel/highlight": ["@babel/highlight@7.25.9", "", { "dependencies": { "@babel/helper-validator-identifier": "^7.25.9", "chalk": "^2.4.2", "js-tokens": "^4.0.0", "picocolors": "^1.0.0" } }, "sha512-llL88JShoCsth8fF8R4SJnIn+WLvR6ccFxu1H3FlMhDontdcmZWf2HgIZ7AIqV3Xcck1idlohrN4EUBQz6klbw=="], - "@babel/runtime": ["@babel/runtime@7.27.1", "", {}, "sha512-1x3D2xEk2fRo3PAhwQwu5UubzgiVWSXTBfWpVd2Mx2AzRqJuDJCsgaDVZ7HB5iGzDW1Hl1sWN2mFyKjmR9uAog=="], "@dr.pogodin/react-helmet": ["@dr.pogodin/react-helmet@3.0.4", "", { "dependencies": { "@babel/runtime": "^7.28.4" }, "peerDependencies": { "react": "19" } }, "sha512-TesfNpzO12qcbyqKyWGDIYTdwVxD3pJv75rE/zhKUq/k9yxeP0BpOdHQ5cc1zA3j/GyY7CuIZjAUXmsxqI1/yw=="], @@ -131,13 +128,27 @@ "@esbuild/win32-x64": ["@esbuild/win32-x64@0.25.4", "", { "os": "win32", "cpu": "x64" }, "sha512-nOT2vZNw6hJ+z43oP1SPea/G/6AbN6X+bGNhNuq8NtRHy4wsMhw765IKLNmnjek7GvjWBYQ8Q5VBoYTFg9y1UQ=="], - "@eslint/eslintrc": ["@eslint/eslintrc@0.4.3", "", { "dependencies": { "ajv": "^6.12.4", "debug": "^4.1.1", "espree": "^7.3.0", "globals": "^13.9.0", "ignore": "^4.0.6", "import-fresh": "^3.2.1", "js-yaml": "^3.13.1", "minimatch": "^3.0.4", "strip-json-comments": "^3.1.1" } }, "sha512-J6KFFz5QCYUJq3pf0mjEcCJVERbzv71PUIDczuh9JkwGEzced6CO5ADLHB1rbf/+oPBtoPfMYNOpGDzCANlbXw=="], + "@eslint-community/eslint-utils": ["@eslint-community/eslint-utils@4.10.1", "", { "dependencies": { "eslint-visitor-keys": "^3.4.3" }, "peerDependencies": { "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" } }, "sha512-cuadcxVFE8sDK6iWJbs8Sn0av2Nrh2QSGQhVlBW9AaAHqHwjWsZHT8LJ4hFGPh7ASBV2deFdM7H/DPjulmh8rg=="], + + "@eslint-community/regexpp": ["@eslint-community/regexpp@4.12.2", "", {}, "sha512-EriSTlt5OC9/7SXkRSCAhfSxxoSUgBm33OH+IkwbdpgoqsSsUg7y3uh+IICI/Qg4BBWr3U2i39RpmycbxMq4ew=="], + + "@eslint/eslintrc": ["@eslint/eslintrc@2.1.4", "", { "dependencies": { "ajv": "^6.12.4", "debug": "^4.3.2", "espree": "^9.6.0", "globals": "^13.19.0", "ignore": "^5.2.0", "import-fresh": "^3.2.1", "js-yaml": "^4.1.0", "minimatch": "^3.1.2", "strip-json-comments": "^3.1.1" } }, "sha512-269Z39MS6wVJtsoUl10L60WdkhJVdPG24Q4eZTH3nnF6lpvSShEK3wQjDX9JRWAUPvPh7COouPpU9IrqaZFvtQ=="], + + "@eslint/js": ["@eslint/js@8.57.1", "", {}, "sha512-d9zaMRSTIKDLhctzH12MtXvJKSSUhaHcjV+2Z+GK+EEY7XKpP5yR4x+N3TAcHTcu963nIr+TMcCb4DBCYX1z6Q=="], "@happy-dom/global-registrator": ["@happy-dom/global-registrator@20.0.10", "", { "dependencies": { "@types/node": "^20.0.0", "happy-dom": "^20.0.10" } }, "sha512-GU0UBt9lJKhZlY/U0Bivj9ZVepDIQoAUupAAl/90THG4/urkzXNglkVYETsnt2pGBDgQ+4vBjMAbLu6XzcKcQA=="], - "@humanwhocodes/config-array": ["@humanwhocodes/config-array@0.5.0", "", { "dependencies": { "@humanwhocodes/object-schema": "^1.2.0", "debug": "^4.1.1", "minimatch": "^3.0.4" } }, "sha512-FagtKFz74XrTl7y6HCzQpwDfXP0yhxe9lHLD1UZxjvZIcbyRz8zTFF/yYNfSfzU414eDwZ1SrO0Qvtyf+wFMQg=="], + "@humanwhocodes/config-array": ["@humanwhocodes/config-array@0.13.0", "", { "dependencies": { "@humanwhocodes/object-schema": "^2.0.3", "debug": "^4.3.1", "minimatch": "^3.0.5" } }, "sha512-DZLEEqFWQFiyK6h5YIeynKx7JlvCYWL0cImfSRXZ9l4Sg2efkFGTuFf6vzXjK1cq6IYkU+Eg/JizXw+TD2vRNw=="], + + "@humanwhocodes/module-importer": ["@humanwhocodes/module-importer@1.0.1", "", {}, "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA=="], + + "@humanwhocodes/object-schema": ["@humanwhocodes/object-schema@2.0.3", "", {}, "sha512-93zYdMES/c1D69yZiKDBj0V24vqNzB/koF26KPaagAfd3P/4gUlh3Dys5ogAK+Exi9QyzlD8x/08Zt7wIKcDcA=="], - "@humanwhocodes/object-schema": ["@humanwhocodes/object-schema@1.2.1", "", {}, "sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA=="], + "@nodelib/fs.scandir": ["@nodelib/fs.scandir@2.1.5", "", { "dependencies": { "@nodelib/fs.stat": "2.0.5", "run-parallel": "^1.1.9" } }, "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g=="], + + "@nodelib/fs.stat": ["@nodelib/fs.stat@2.0.5", "", {}, "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A=="], + + "@nodelib/fs.walk": ["@nodelib/fs.walk@1.2.8", "", { "dependencies": { "@nodelib/fs.scandir": "2.1.5", "fastq": "^1.6.0" } }, "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg=="], "@oven/bun-darwin-aarch64": ["@oven/bun-darwin-aarch64@1.3.3", "", { "os": "darwin", "cpu": "arm64" }, "sha512-eJopQrUk0WR7jViYDC29+Rp50xGvs4GtWOXBeqCoFMzutkkO3CZvHehA4JqnjfWMTSS8toqvRhCSOpOz62Wf9w=="], @@ -187,24 +198,22 @@ "@types/whatwg-mimetype": ["@types/whatwg-mimetype@3.0.2", "", {}, "sha512-c2AKvDT8ToxLIOUlN51gTiHXflsfIFisS4pO7pDPoKouJCESkhZnEy623gwP9laCy5lnLDAw1vAzu2vM2YLOrA=="], - "acorn": ["acorn@7.4.1", "", { "bin": { "acorn": "bin/acorn" } }, "sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A=="], + "@ungap/structured-clone": ["@ungap/structured-clone@1.4.0", "", {}, "sha512-1mEZtMKPM09vDmQt5y7YvmN2+DFTP7Tg0EWXdic8/C6VRnpb33e4ghisCIE3WZjsE2N8mf+QV1Zqh7ZFYLWInQ=="], + + "acorn": ["acorn@8.18.0", "", { "bin": { "acorn": "bin/acorn" } }, "sha512-lGq+9yr1/GuAWaVYIHRjvvySG5/4VfKIvC8EWxStPdcDh/Ka7FG3twP6v4d5BkravUilhIAsG4Qj83t02LWUPQ=="], "acorn-jsx": ["acorn-jsx@5.3.2", "", { "peerDependencies": { "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" } }, "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ=="], "ajv": ["ajv@6.12.6", "", { "dependencies": { "fast-deep-equal": "^3.1.1", "fast-json-stable-stringify": "^2.0.0", "json-schema-traverse": "^0.4.1", "uri-js": "^4.2.2" } }, "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g=="], - "ansi-colors": ["ansi-colors@4.1.3", "", {}, "sha512-/6w/C21Pm1A7aZitlI5Ni/2J6FFQN8i1Cvz3kHABAAbw93v/NlvKdVOqz7CCWz/3iv/JplRSEEZ83XION15ovw=="], - "ansi-regex": ["ansi-regex@5.0.1", "", {}, "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ=="], "ansi-styles": ["ansi-styles@4.3.0", "", { "dependencies": { "color-convert": "^2.0.1" } }, "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg=="], - "argparse": ["argparse@1.0.10", "", { "dependencies": { "sprintf-js": "~1.0.2" } }, "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg=="], + "argparse": ["argparse@2.0.1", "", {}, "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q=="], "aria-query": ["aria-query@5.3.0", "", { "dependencies": { "dequal": "^2.0.3" } }, "sha512-b0P0sZPKtyu8HkeRAfCq0IfURZK+SuwMjY1UXGBU27wpAiTwQAIlq56IbIO+ytk/JjS1fMR14ee5WBBfKi5J6A=="], - "astral-regex": ["astral-regex@2.0.0", "", {}, "sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ=="], - "balanced-match": ["balanced-match@1.0.2", "", {}, "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw=="], "brace-expansion": ["brace-expansion@1.1.11", "", { "dependencies": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" } }, "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA=="], @@ -259,8 +268,6 @@ "emoji-regex": ["emoji-regex@8.0.0", "", {}, "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A=="], - "enquirer": ["enquirer@2.4.1", "", { "dependencies": { "ansi-colors": "^4.1.1", "strip-ansi": "^6.0.1" } }, "sha512-rRqJg/6gd538VHvR3PSrdRBb/1Vy2YfzHqzvbhGIQpDRKIa4FgV/54b5Q1xYSxOOwKvjXweS26E0Q+nAMwp2pQ=="], - "error-ex": ["error-ex@1.3.2", "", { "dependencies": { "is-arrayish": "^0.2.1" } }, "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g=="], "esbuild": ["esbuild@0.25.4", "", { "optionalDependencies": { "@esbuild/aix-ppc64": "0.25.4", "@esbuild/android-arm": "0.25.4", "@esbuild/android-arm64": "0.25.4", "@esbuild/android-x64": "0.25.4", "@esbuild/darwin-arm64": "0.25.4", "@esbuild/darwin-x64": "0.25.4", "@esbuild/freebsd-arm64": "0.25.4", "@esbuild/freebsd-x64": "0.25.4", "@esbuild/linux-arm": "0.25.4", "@esbuild/linux-arm64": "0.25.4", "@esbuild/linux-ia32": "0.25.4", "@esbuild/linux-loong64": "0.25.4", "@esbuild/linux-mips64el": "0.25.4", "@esbuild/linux-ppc64": "0.25.4", "@esbuild/linux-riscv64": "0.25.4", "@esbuild/linux-s390x": "0.25.4", "@esbuild/linux-x64": "0.25.4", "@esbuild/netbsd-arm64": "0.25.4", "@esbuild/netbsd-x64": "0.25.4", "@esbuild/openbsd-arm64": "0.25.4", "@esbuild/openbsd-x64": "0.25.4", "@esbuild/sunos-x64": "0.25.4", "@esbuild/win32-arm64": "0.25.4", "@esbuild/win32-ia32": "0.25.4", "@esbuild/win32-x64": "0.25.4" }, "bin": { "esbuild": "bin/esbuild" } }, "sha512-8pgjLUcUjcgDg+2Q4NYXnPbo/vncAY4UmyaCm0jZevERqCHZIaWwdJHkf8XQtu4AxSKCdvrUbT0XUr1IdZzI8Q=="], @@ -269,25 +276,21 @@ "escape-string-regexp": ["escape-string-regexp@4.0.0", "", {}, "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA=="], - "eslint": ["eslint@7.32.0", "", { "dependencies": { "@babel/code-frame": "7.12.11", "@eslint/eslintrc": "^0.4.3", "@humanwhocodes/config-array": "^0.5.0", "ajv": "^6.10.0", "chalk": "^4.0.0", "cross-spawn": "^7.0.2", "debug": "^4.0.1", "doctrine": "^3.0.0", "enquirer": "^2.3.5", "escape-string-regexp": "^4.0.0", "eslint-scope": "^5.1.1", "eslint-utils": "^2.1.0", "eslint-visitor-keys": "^2.0.0", "espree": "^7.3.1", "esquery": "^1.4.0", "esutils": "^2.0.2", "fast-deep-equal": "^3.1.3", "file-entry-cache": "^6.0.1", "functional-red-black-tree": "^1.0.1", "glob-parent": "^5.1.2", "globals": "^13.6.0", "ignore": "^4.0.6", "import-fresh": "^3.0.0", "imurmurhash": "^0.1.4", "is-glob": "^4.0.0", "js-yaml": "^3.13.1", "json-stable-stringify-without-jsonify": "^1.0.1", "levn": "^0.4.1", "lodash.merge": "^4.6.2", "minimatch": "^3.0.4", "natural-compare": "^1.4.0", "optionator": "^0.9.1", "progress": "^2.0.0", "regexpp": "^3.1.0", "semver": "^7.2.1", "strip-ansi": "^6.0.0", "strip-json-comments": "^3.1.0", "table": "^6.0.9", "text-table": "^0.2.0", "v8-compile-cache": "^2.0.3" }, "bin": { "eslint": "bin/eslint.js" } }, "sha512-VHZ8gX+EDfz+97jGcgyGCyRia/dPOd6Xh9yPv8Bl1+SoaIwD+a/vlrOmGRUyOYu7MwUhc7CxqeaDZU13S4+EpA=="], + "eslint": ["eslint@8.57.1", "", { "dependencies": { "@eslint-community/eslint-utils": "^4.2.0", "@eslint-community/regexpp": "^4.6.1", "@eslint/eslintrc": "^2.1.4", "@eslint/js": "8.57.1", "@humanwhocodes/config-array": "^0.13.0", "@humanwhocodes/module-importer": "^1.0.1", "@nodelib/fs.walk": "^1.2.8", "@ungap/structured-clone": "^1.2.0", "ajv": "^6.12.4", "chalk": "^4.0.0", "cross-spawn": "^7.0.2", "debug": "^4.3.2", "doctrine": "^3.0.0", "escape-string-regexp": "^4.0.0", "eslint-scope": "^7.2.2", "eslint-visitor-keys": "^3.4.3", "espree": "^9.6.1", "esquery": "^1.4.2", "esutils": "^2.0.2", "fast-deep-equal": "^3.1.3", "file-entry-cache": "^6.0.1", "find-up": "^5.0.0", "glob-parent": "^6.0.2", "globals": "^13.19.0", "graphemer": "^1.4.0", "ignore": "^5.2.0", "imurmurhash": "^0.1.4", "is-glob": "^4.0.0", "is-path-inside": "^3.0.3", "js-yaml": "^4.1.0", "json-stable-stringify-without-jsonify": "^1.0.1", "levn": "^0.4.1", "lodash.merge": "^4.6.2", "minimatch": "^3.1.2", "natural-compare": "^1.4.0", "optionator": "^0.9.3", "strip-ansi": "^6.0.1", "text-table": "^0.2.0" }, "bin": { "eslint": "bin/eslint.js" } }, "sha512-ypowyDxpVSYpkXr9WPv2PAZCtNip1Mv5KTW0SCurXv/9iOpcrH9PaqUElksqEB6pChqHGDRCFTyrZlGhnLNGiA=="], "eslint-plugin-react-hooks": ["eslint-plugin-react-hooks@4.6.2", "", { "peerDependencies": { "eslint": "^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0" } }, "sha512-QzliNJq4GinDBcD8gPB5v0wh6g8q3SUi6EFF0x8N/BL9PoVs0atuGc47ozMRyOWAKdwaZ5OnbOEa3WR+dSGKuQ=="], - "eslint-scope": ["eslint-scope@5.1.1", "", { "dependencies": { "esrecurse": "^4.3.0", "estraverse": "^4.1.1" } }, "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw=="], - - "eslint-utils": ["eslint-utils@2.1.0", "", { "dependencies": { "eslint-visitor-keys": "^1.1.0" } }, "sha512-w94dQYoauyvlDc43XnGB8lU3Zt713vNChgt4EWwhXAP2XkBvndfxF0AgIqKOOasjPIPzj9JqgwkwbCYD0/V3Zg=="], - - "eslint-visitor-keys": ["eslint-visitor-keys@2.1.0", "", {}, "sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw=="], + "eslint-scope": ["eslint-scope@7.2.2", "", { "dependencies": { "esrecurse": "^4.3.0", "estraverse": "^5.2.0" } }, "sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg=="], - "espree": ["espree@7.3.1", "", { "dependencies": { "acorn": "^7.4.0", "acorn-jsx": "^5.3.1", "eslint-visitor-keys": "^1.3.0" } }, "sha512-v3JCNCE64umkFpmkFGqzVKsOT0tN1Zr+ueqLZfpV1Ob8e+CEgPWa+OxCoGH3tnhimMKIaBm4m/vaRpJ/krRz2g=="], + "eslint-visitor-keys": ["eslint-visitor-keys@3.4.3", "", {}, "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag=="], - "esprima": ["esprima@4.0.1", "", { "bin": { "esparse": "./bin/esparse.js", "esvalidate": "./bin/esvalidate.js" } }, "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A=="], + "espree": ["espree@9.6.1", "", { "dependencies": { "acorn": "^8.9.0", "acorn-jsx": "^5.3.2", "eslint-visitor-keys": "^3.4.1" } }, "sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ=="], "esquery": ["esquery@1.6.0", "", { "dependencies": { "estraverse": "^5.1.0" } }, "sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg=="], "esrecurse": ["esrecurse@4.3.0", "", { "dependencies": { "estraverse": "^5.2.0" } }, "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag=="], - "estraverse": ["estraverse@4.3.0", "", {}, "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw=="], + "estraverse": ["estraverse@5.3.0", "", {}, "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA=="], "esutils": ["esutils@2.0.3", "", {}, "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g=="], @@ -297,7 +300,7 @@ "fast-levenshtein": ["fast-levenshtein@2.0.6", "", {}, "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw=="], - "fast-uri": ["fast-uri@3.0.6", "", {}, "sha512-Atfo14OibSv5wAp4VWNsFYE1AchQRTv9cBGWET4pZWHzYshFSS9NQI6I57rdKn9croWVMbYFbLhJ+yJvmZIIHw=="], + "fastq": ["fastq@1.20.3", "", { "dependencies": { "reusify": "^1.0.4" } }, "sha512-XKv5nnLs6nLF71NgiKJLIZFLkPyIEuOselLG7ujZnGrRfQK8HpvY+WqKhAJUAdLomwVHErVS4LfxFlPq0/FTAw=="], "fdir": ["fdir@6.4.4", "", { "peerDependencies": { "picomatch": "^3 || ^4" }, "optionalPeers": ["picomatch"] }, "sha512-1NZP+GK4GfuAv3PqKvxQRDMjdSRZjnkq7KfhlNrCNNlZ0ygQFpebfrnfnq/W7fpUnAv9aGWmY1zKx7FYL3gwhg=="], @@ -313,23 +316,23 @@ "fs.realpath": ["fs.realpath@1.0.0", "", {}, "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw=="], - "functional-red-black-tree": ["functional-red-black-tree@1.0.1", "", {}, "sha512-dsKNQNdj6xA3T+QlADDA7mOSlX0qiMINjn0cgr+eGHGsbSHzTabcIogz2+p/iqP1Xs6EP/sS2SbqH+brGTbq0g=="], - "get-caller-file": ["get-caller-file@2.0.5", "", {}, "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg=="], "glob": ["glob@7.2.3", "", { "dependencies": { "fs.realpath": "^1.0.0", "inflight": "^1.0.4", "inherits": "2", "minimatch": "^3.1.1", "once": "^1.3.0", "path-is-absolute": "^1.0.0" } }, "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q=="], - "glob-parent": ["glob-parent@5.1.2", "", { "dependencies": { "is-glob": "^4.0.1" } }, "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow=="], + "glob-parent": ["glob-parent@6.0.2", "", { "dependencies": { "is-glob": "^4.0.3" } }, "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A=="], "globals": ["globals@13.24.0", "", { "dependencies": { "type-fest": "^0.20.2" } }, "sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ=="], + "graphemer": ["graphemer@1.4.0", "", {}, "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag=="], + "happy-dom": ["happy-dom@20.0.10", "", { "dependencies": { "@types/node": "^20.0.0", "@types/whatwg-mimetype": "^3.0.2", "whatwg-mimetype": "^3.0.0" } }, "sha512-6umCCHcjQrhP5oXhrHQQvLB0bwb1UzHAHdsXy+FjtKoYjUhmNZsQL8NivwM1vDvNEChJabVrUYxUnp/ZdYmy2g=="], "has-flag": ["has-flag@4.0.0", "", {}, "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ=="], "husky": ["husky@4.3.8", "", { "dependencies": { "chalk": "^4.0.0", "ci-info": "^2.0.0", "compare-versions": "^3.6.0", "cosmiconfig": "^7.0.0", "find-versions": "^4.0.0", "opencollective-postinstall": "^2.0.2", "pkg-dir": "^5.0.0", "please-upgrade-node": "^3.2.0", "slash": "^3.0.0", "which-pm-runs": "^1.0.0" }, "bin": { "husky-run": "bin/run.js", "husky-upgrade": "lib/upgrader/bin.js" } }, "sha512-LCqqsB0PzJQ/AlCgfrfzRe3e3+NvmefAdKQhRYpxS4u6clblBoDdzzvHi8fmxKRzvMxPY/1WZWzomPZww0Anow=="], - "ignore": ["ignore@4.0.6", "", {}, "sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg=="], + "ignore": ["ignore@5.3.2", "", {}, "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g=="], "import-fresh": ["import-fresh@3.3.1", "", { "dependencies": { "parent-module": "^1.0.0", "resolve-from": "^4.0.0" } }, "sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ=="], @@ -349,6 +352,8 @@ "is-glob": ["is-glob@4.0.3", "", { "dependencies": { "is-extglob": "^2.1.1" } }, "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg=="], + "is-path-inside": ["is-path-inside@3.0.3", "", {}, "sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ=="], + "isarray": ["isarray@0.0.1", "", {}, "sha512-D2S+3GLxWH+uhrNEcoh/fnmYeP8E8/zHl644d/jdA0g2uyXvy3sb0qxotE+ne0LtccHknQzWwZEzhak7oJ0COQ=="], "isexe": ["isexe@2.0.0", "", {}, "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw=="], @@ -357,7 +362,7 @@ "js-tokens": ["js-tokens@4.0.0", "", {}, "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ=="], - "js-yaml": ["js-yaml@3.14.1", "", { "dependencies": { "argparse": "^1.0.7", "esprima": "^4.0.0" }, "bin": { "js-yaml": "bin/js-yaml.js" } }, "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g=="], + "js-yaml": ["js-yaml@4.3.2", "", { "dependencies": { "argparse": "^2.0.1" }, "bin": { "js-yaml": "bin/js-yaml.js" } }, "sha512-SFNOvSJ+Dgf/9An904Yx+CgSlIPCkIpao4qo51lpee25TIRejdH3rhR4EZMGoNx3/TP3O+wzWuiTFl4sqbltzA=="], "json-buffer": ["json-buffer@3.0.1", "", {}, "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ=="], @@ -381,8 +386,6 @@ "lodash.merge": ["lodash.merge@4.6.2", "", {}, "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ=="], - "lodash.truncate": ["lodash.truncate@4.4.2", "", {}, "sha512-jttmRe7bRse52OsWIMDLaXxWqRAmtIUccAQ3garviCqJjafXOfNMO0yMfNpdD6zbGaTU0P5Nz7e7gAT6cKmJRw=="], - "lz-string": ["lz-string@1.5.0", "", { "bin": { "lz-string": "bin/bin.js" } }, "sha512-h5bgJWpxJNswbU7qCrV0tIKQCaS3blPDrqKWx+QxzuzL1zGUzij9XCWLrSLsJPu5t+eWA/ycetzYAO5IOMcWAQ=="], "magazin": ["magazin@workspace:packages/magazin"], @@ -447,10 +450,10 @@ "process-nextick-args": ["process-nextick-args@2.0.1", "", {}, "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag=="], - "progress": ["progress@2.0.3", "", {}, "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA=="], - "punycode": ["punycode@2.3.1", "", {}, "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg=="], + "queue-microtask": ["queue-microtask@1.2.3", "", {}, "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A=="], + "react": ["react@19.2.0", "", {}, "sha512-tmbWg6W31tQLeB5cdIBOicJDJRR2KzXsV7uSK9iNfLWQ5bIZfxuPEHp7M8wiHyHnn0DD1i7w3Zmin0FtkrwoCQ=="], "react-dom": ["react-dom@19.2.0", "", { "dependencies": { "scheduler": "^0.27.0" }, "peerDependencies": { "react": "^19.2.0" } }, "sha512-UlbRu4cAiGaIewkPyiRGJk0imDN2T3JjieT6spoL2UeSf5od4n5LB/mQ4ejmxhCFT1tYe8IvaFulzynWovsEFQ=="], @@ -465,22 +468,20 @@ "regexparam": ["regexparam@3.0.0", "", {}, "sha512-RSYAtP31mvYLkAHrOlh25pCNQ5hWnT106VukGaaFfuJrZFkGRX5GhUAdPqpSDXxOhA2c4akmRuplv1mRqnBn6Q=="], - "regexpp": ["regexpp@3.2.0", "", {}, "sha512-pq2bWo9mVD43nbts2wGv17XLiNLya+GklZ8kaDLV2Z08gDCsGpnKn9BFMepvWuHCbyVvY7J5o5+BVvoQbmlJLg=="], - "require-directory": ["require-directory@2.1.1", "", {}, "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q=="], - "require-from-string": ["require-from-string@2.0.2", "", {}, "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw=="], - "resolve-from": ["resolve-from@4.0.0", "", {}, "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g=="], + "reusify": ["reusify@1.1.0", "", {}, "sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw=="], + "rimraf": ["rimraf@3.0.2", "", { "dependencies": { "glob": "^7.1.3" }, "bin": { "rimraf": "bin.js" } }, "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA=="], + "run-parallel": ["run-parallel@1.2.0", "", { "dependencies": { "queue-microtask": "^1.2.2" } }, "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA=="], + "safe-buffer": ["safe-buffer@5.1.2", "", {}, "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g=="], "scheduler": ["scheduler@0.27.0", "", {}, "sha512-eNv+WrVbKu1f3vbYJT/xtiF5syA5HPIMtf9IgY/nKg0sWqzAUEvqY/xm7OcZc/qafLx/iO9FgOmeSAp4v5ti/Q=="], - "semver": ["semver@7.7.2", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA=="], - "semver-compare": ["semver-compare@1.0.0", "", {}, "sha512-YM3/ITh2MJ5MtzaM429anh+x2jiLVjqILF4m4oyQB18W7Ggea7BfqdH/wGMK7dDiMghv/6WG7znWMwUDzJiXow=="], "semver-regex": ["semver-regex@3.1.4", "", {}, "sha512-6IiqeZNgq01qGf0TId0t3NvKzSvUsjcpdEO3AQNeIjR6A2+ckTnQlDpl4qu1bjRv0RzN3FP9hzFmws3lKqRWkA=="], @@ -493,10 +494,6 @@ "slash": ["slash@3.0.0", "", {}, "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q=="], - "slice-ansi": ["slice-ansi@4.0.0", "", { "dependencies": { "ansi-styles": "^4.0.0", "astral-regex": "^2.0.0", "is-fullwidth-code-point": "^3.0.0" } }, "sha512-qMCMfhY040cVHT43K9BFygqYbUPFZKHOg7K73mtTWJRb8pyP3fzf4Ixd5SzdEJQ6MRUg/WBnOLxghZtKKurENQ=="], - - "sprintf-js": ["sprintf-js@1.0.3", "", {}, "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g=="], - "string-width": ["string-width@4.2.3", "", { "dependencies": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", "strip-ansi": "^6.0.1" } }, "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g=="], "string_decoder": ["string_decoder@0.10.31", "", {}, "sha512-ev2QzSzWPYmy9GuqfIVildA4OdcGLeFZQrq5ys6RtiuF+RQQiZWr8TZNyAcuVXyQRYfEO+MsoB/1BuQVhOJuoQ=="], @@ -509,8 +506,6 @@ "supports-color": ["supports-color@7.2.0", "", { "dependencies": { "has-flag": "^4.0.0" } }, "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw=="], - "table": ["table@6.9.0", "", { "dependencies": { "ajv": "^8.0.1", "lodash.truncate": "^4.4.2", "slice-ansi": "^4.0.0", "string-width": "^4.2.3", "strip-ansi": "^6.0.1" } }, "sha512-9kY+CygyYM6j02t5YFHbNz2FN5QmYGv9zAjVp4lCDjlCw7amdckXlEt/bjMhUIfj4ThGRE4gCUH5+yGnNuPo5A=="], - "tailwindcss": ["tailwindcss@4.1.17", "", {}, "sha512-j9Ee2YjuQqYT9bbRTfTZht9W/ytp5H+jJpZKiYdP/bpnXARAuELt9ofP0lPnmHjbga7SNQIxdTAXCmtKVYjN+Q=="], "text-table": ["text-table@0.2.0", "", {}, "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw=="], @@ -531,12 +526,8 @@ "uri-js": ["uri-js@4.4.1", "", { "dependencies": { "punycode": "^2.1.0" } }, "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg=="], - "use-sync-external-store": ["use-sync-external-store@1.5.0", "", { "peerDependencies": { "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" } }, "sha512-Rb46I4cGGVBmjamjphe8L/UnvJD+uPPtTkNvX5mZgqdbavhI4EbgIWJiIHXJ8bc/i9EQGPRh4DwEURJ552Do0A=="], - "util-deprecate": ["util-deprecate@1.0.2", "", {}, "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw=="], - "v8-compile-cache": ["v8-compile-cache@2.4.0", "", {}, "sha512-ocyWc3bAHBB/guyqJQVI5o4BZkPhznPYUG2ea80Gond/BgNWpap8TOmLSeeQG7bnh2KMISxskdADG59j7zruhw=="], - "whatwg-mimetype": ["whatwg-mimetype@3.0.0", "", {}, "sha512-nt+N2dzIutVRxARx1nghPKGv1xHikU7HKdfafKkLNLindmPU/ch3U31NOCGGA/dmPcmb1VlofO0vnKAcsm0o/Q=="], "which": ["which@2.0.2", "", { "dependencies": { "isexe": "^2.0.0" }, "bin": { "node-which": "./bin/node-which" } }, "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA=="], @@ -565,8 +556,6 @@ "yocto-queue": ["yocto-queue@0.1.0", "", {}, "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q=="], - "@babel/highlight/chalk": ["chalk@2.4.2", "", { "dependencies": { "ansi-styles": "^3.2.1", "escape-string-regexp": "^1.0.5", "supports-color": "^5.3.0" } }, "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ=="], - "@dr.pogodin/react-helmet/@babel/runtime": ["@babel/runtime@7.28.4", "", {}, "sha512-Q/N6JNWvIvPnLDvjlE1OUBLPQHH6l3CltCEsHIujp45zQUSSh8K+gHnaEX45yAT1nyngnINhvWtzN+Nb9D8RAQ=="], "@testing-library/jest-dom/aria-query": ["aria-query@5.3.2", "", {}, "sha512-COROpnaoap1E2F000S62r6A60uHZnmlvomhfyT2DlTcrY1OrBKn2UhH7qn5wTC9zMvD0AY7csdPSNwKP+7WiQw=="], @@ -577,44 +566,18 @@ "bun-types/@types/node": ["@types/node@22.15.21", "", { "dependencies": { "undici-types": "~6.21.0" } }, "sha512-EV/37Td6c+MgKAbkcLG6vqZ2zEYHD7bvSrzqqs2RIhbA6w3x+Dqz8MZM3sP6kGTeLrdoOgKZe+Xja7tUB2DNkQ=="], - "eslint/@babel/code-frame": ["@babel/code-frame@7.12.11", "", { "dependencies": { "@babel/highlight": "^7.10.4" } }, "sha512-Zt1yodBx1UcyiePMSkWnU4hPqhwq7hGi2nFL1LeA3EUl+q2LQx16MISgJ0+z7dnmgvP9QtIleuETGOiOH1RcIw=="], - - "eslint-utils/eslint-visitor-keys": ["eslint-visitor-keys@1.3.0", "", {}, "sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ=="], - - "espree/eslint-visitor-keys": ["eslint-visitor-keys@1.3.0", "", {}, "sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ=="], - - "esquery/estraverse": ["estraverse@5.3.0", "", {}, "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA=="], - - "esrecurse/estraverse": ["estraverse@5.3.0", "", {}, "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA=="], - "magazin/@types/bun": ["@types/bun@1.3.4", "", { "dependencies": { "bun-types": "1.3.4" } }, "sha512-EEPTKXHP+zKGPkhRLv+HI0UEX8/o+65hqARxLy8Ov5rIxMBPNTjeZww00CIihrIQGEQBYg+0roO5qOnS/7boGA=="], "pretty-format/ansi-styles": ["ansi-styles@5.2.0", "", {}, "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA=="], - "table/ajv": ["ajv@8.17.1", "", { "dependencies": { "fast-deep-equal": "^3.1.3", "fast-uri": "^3.0.1", "json-schema-traverse": "^1.0.0", "require-from-string": "^2.0.2" } }, "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g=="], - "through2/readable-stream": ["readable-stream@2.3.8", "", { "dependencies": { "core-util-is": "~1.0.0", "inherits": "~2.0.3", "isarray": "~1.0.0", "process-nextick-args": "~2.0.0", "safe-buffer": "~5.1.1", "string_decoder": "~1.1.1", "util-deprecate": "~1.0.1" } }, "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA=="], - "@babel/highlight/chalk/ansi-styles": ["ansi-styles@3.2.1", "", { "dependencies": { "color-convert": "^1.9.0" } }, "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA=="], - - "@babel/highlight/chalk/escape-string-regexp": ["escape-string-regexp@1.0.5", "", {}, "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg=="], - - "@babel/highlight/chalk/supports-color": ["supports-color@5.5.0", "", { "dependencies": { "has-flag": "^3.0.0" } }, "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow=="], - "magazin/@types/bun/bun-types": ["bun-types@1.3.4", "", { "dependencies": { "@types/node": "*" } }, "sha512-5ua817+BZPZOlNaRgGBpZJOSAQ9RQ17pkwPD0yR7CfJg+r8DgIILByFifDTa+IPDDxzf5VNhtNlcKqFzDgJvlQ=="], - "table/ajv/json-schema-traverse": ["json-schema-traverse@1.0.0", "", {}, "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug=="], - "through2/readable-stream/isarray": ["isarray@1.0.0", "", {}, "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ=="], "through2/readable-stream/string_decoder": ["string_decoder@1.1.1", "", { "dependencies": { "safe-buffer": "~5.1.0" } }, "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg=="], - "@babel/highlight/chalk/ansi-styles/color-convert": ["color-convert@1.9.3", "", { "dependencies": { "color-name": "1.1.3" } }, "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg=="], - - "@babel/highlight/chalk/supports-color/has-flag": ["has-flag@3.0.0", "", {}, "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw=="], - "magazin/@types/bun/bun-types/@types/node": ["@types/node@22.15.21", "", { "dependencies": { "undici-types": "~6.21.0" } }, "sha512-EV/37Td6c+MgKAbkcLG6vqZ2zEYHD7bvSrzqqs2RIhbA6w3x+Dqz8MZM3sP6kGTeLrdoOgKZe+Xja7tUB2DNkQ=="], - - "@babel/highlight/chalk/ansi-styles/color-convert/color-name": ["color-name@1.1.3", "", {}, "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw=="], } } diff --git a/bunfig.toml b/bunfig.toml index ede45b06..7e2a45f0 100644 --- a/bunfig.toml +++ b/bunfig.toml @@ -3,6 +3,8 @@ preload = ["./packages/wouter/test/setup.ts"] coverageSkipTestFiles = true coveragePathIgnorePatterns = [ "**/test/**", + # Magazin is a demo app; its behavior tests should not dilute library coverage. + "packages/magazin/**", # temporary copies of packages/wouter/src/* created by preact.test.tsx; # already measured at their original path (react-deps.js is preact-specific and stays measured) "packages/wouter-preact/src/index.js", @@ -10,7 +12,5 @@ coveragePathIgnorePatterns = [ "packages/wouter-preact/src/memory-location.js", "packages/wouter-preact/src/use-browser-location.js", "packages/wouter-preact/src/use-hash-location.js", - "packages/wouter-preact/src/use-sync-external-store.js", - "packages/wouter-preact/src/use-sync-external-store.native.js", ] coverageThreshold = { lines = 0.99 } diff --git a/package.json b/package.json index c35342eb..dc8e492f 100644 --- a/package.json +++ b/package.json @@ -10,8 +10,10 @@ "fix:p": "prettier --write \"./**/*.(js|ts){x,}\"", "test": "bun test", "test-types": "tsc --noEmit", + "test-types:consumer": "tsc -p tsconfig.consumer.json && tsc -p tsconfig.consumer.json --module NodeNext --moduleResolution NodeNext && tsc -p tsconfig.consumer.json --moduleResolution Node", "size": "size-limit", - "lint": "eslint packages/**/*.js", + "size:dependencies": "bun scripts/measure-size.ts", + "lint": "eslint packages/wouter/src/*.js packages/wouter-preact/src/react-deps.js", "build": ":" }, "author": "Alexey Taktarov ", @@ -30,40 +32,36 @@ "size-limit": [ { "path": "packages/wouter/src/index.js", - "limit": "2500 B", + "limit": "2200 B", "ignore": [ - "react", - "use-sync-external-store" + "react" ] }, { "path": "packages/wouter/src/use-browser-location.js", - "limit": "1000 B", + "limit": "400 B", "import": "{ useBrowserLocation }", "ignore": [ - "react", - "use-sync-external-store" + "react" ] }, { "path": "packages/wouter/src/memory-location.js", - "limit": "1000 B", + "limit": "550 B", "ignore": [ - "react", - "use-sync-external-store" + "react" ] }, { "path": "packages/wouter/src/use-hash-location.js", - "limit": "1000 B", + "limit": "500 B", "ignore": [ - "react", - "use-sync-external-store" + "react" ] }, { "path": "packages/wouter-preact/src/index.js", - "limit": "2500 B", + "limit": "2300 B", "ignore": [ "preact", "preact/hooks" @@ -71,7 +69,7 @@ }, { "path": "packages/wouter-preact/src/use-browser-location.js", - "limit": "1000 B", + "limit": "500 B", "import": "{ useBrowserLocation }", "ignore": [ "preact", @@ -80,7 +78,7 @@ }, { "path": "packages/wouter-preact/src/use-hash-location.js", - "limit": "1000 B", + "limit": "650 B", "ignore": [ "preact", "preact/hooks" @@ -88,7 +86,7 @@ }, { "path": "packages/wouter-preact/src/memory-location.js", - "limit": "1000 B", + "limit": "700 B", "ignore": [ "preact", "preact/hooks" @@ -97,19 +95,20 @@ ], "husky": { "hooks": { - "commit-msg": "npm run fix:p" + "commit-msg": "bun run fix:p" } }, "eslintConfig": { "extends": "eslint:recommended", "parserOptions": { + "ecmaVersion": 2022, "sourceType": "module", "ecmaFeatures": { "jsx": true } }, "env": { - "es2020": true, + "es2022": true, "browser": true, "node": true }, @@ -141,7 +140,7 @@ "@types/react": "^19", "@types/react-dom": "^19", "copyfiles": "^2.4.1", - "eslint": "^7.19.0", + "eslint": "^8.57.1", "eslint-plugin-react-hooks": "^4.6.2", "happy-dom": "^20.0.10", "husky": "^4.3.0", diff --git a/packages/magazin/App.tsx b/packages/magazin/App.tsx index 643f3b8d..ff5eec93 100644 --- a/packages/magazin/App.tsx +++ b/packages/magazin/App.tsx @@ -1,20 +1,51 @@ +import { useState } from "react"; import { Route, Switch, Redirect } from "wouter"; import { Helmet } from "@dr.pogodin/react-helmet"; import { HomePage } from "@/routes/home.tsx"; import { AboutPage } from "@/routes/about.tsx"; import { NotFoundPage } from "@/routes/404.tsx"; import { ProductPage } from "@/routes/products/[slug].tsx"; -import { CartPage } from "@/routes/cart.tsx"; +import { CartPage, type CartItem } from "@/routes/cart.tsx"; import { WithStatusCode } from "@/components/with-status-code.tsx"; import { Navbar } from "@/components/navbar.tsx"; import { Footer } from "@/components/footer.tsx"; +import { products, type Product } from "@/db/products.ts"; + +const initialCartItems: CartItem[] = [ + { product: products[4]!, quantity: 1 }, + { product: products[5]!, quantity: 1 }, + { product: products[0]!, quantity: 2 }, + { product: products[7]!, quantity: 3 }, +]; export function App() { + const [cartItems, setCartItems] = useState(initialCartItems); + + const addToCart = (product: Product) => { + setCartItems((items) => { + const existingItem = items.find( + (item) => item.product.slug === product.slug + ); + + if (!existingItem) { + return [...items, { product, quantity: 1 }]; + } + + return items.map((item) => + item.product.slug === product.slug + ? { ...item, quantity: item.quantity + 1 } + : item + ); + }); + }; + + const cartCount = cartItems.reduce((count, item) => count + item.quantity, 0); + return (
- +
@@ -28,11 +59,13 @@ export function App() { - {(params) => } + {(params) => ( + + )} - + diff --git a/packages/magazin/accessibility.regression-1.test.tsx b/packages/magazin/accessibility.regression-1.test.tsx new file mode 100644 index 00000000..d7a0dd39 --- /dev/null +++ b/packages/magazin/accessibility.regression-1.test.tsx @@ -0,0 +1,42 @@ +import { expect, test } from "bun:test"; +import { render } from "@testing-library/react"; +import { HelmetProvider } from "@dr.pogodin/react-helmet"; + +import { App } from "./App"; + +function renderAppAt(path: string) { + history.replaceState(null, "", path); + return render( + + + + ); +} + +// Regression: ISSUE-005 — icon-only links had empty or numeric-only names +// Found by /qa on 2026-09-05 +// Report: .gstack/qa-reports/qa-report-localhost-3002-2026-09-05.md +test("names icon-only product navigation links", () => { + const { getByRole } = renderAppAt("/products/silver-ok-ring"); + + expect(getByRole("link", { name: "Shopping cart, 7 items" })).toBeTruthy(); + expect(getByRole("link", { name: "Back to products" })).toBeTruthy(); +}); + +// Regression: ISSUE-007 — filters and sort omitted accessible state and purpose +// Found by /qa on 2026-09-05 +// Report: .gstack/qa-reports/qa-report-localhost-3002-2026-09-05.md +test("exposes the selected category and sort control purpose", () => { + const { getByRole } = renderAppAt("/?category=accessories&sort=price-desc"); + + expect( + getByRole("button", { name: "Accessories" }).getAttribute("aria-pressed") + ).toBe("true"); + expect( + getByRole("button", { name: "All" }).getAttribute("aria-pressed") + ).toBe("false"); + expect( + (getByRole("combobox", { name: "Sort products" }) as HTMLSelectElement) + .value + ).toBe("price-desc"); +}); diff --git a/packages/magazin/bun.lock b/packages/magazin/bun.lock deleted file mode 100644 index d419404a..00000000 --- a/packages/magazin/bun.lock +++ /dev/null @@ -1,45 +0,0 @@ -{ - "lockfileVersion": 1, - "configVersion": 1, - "workspaces": { - "": { - "name": "sourdough", - "dependencies": { - "react": "19", - "react-dom": "19", - "wouter": "3.8.0", - }, - "devDependencies": { - "@types/bun": "latest", - }, - "peerDependencies": { - "typescript": "^5", - }, - }, - }, - "packages": { - "@types/bun": ["@types/bun@1.3.3", "", { "dependencies": { "bun-types": "1.3.3" } }, "sha512-ogrKbJ2X5N0kWLLFKeytG0eHDleBYtngtlbu9cyBKFtNL3cnpDZkNdQj8flVf6WTZUX5ulI9AY1oa7ljhSrp+g=="], - - "@types/node": ["@types/node@24.10.1", "", { "dependencies": { "undici-types": "~7.16.0" } }, "sha512-GNWcUTRBgIRJD5zj+Tq0fKOJ5XZajIiBroOF0yvj2bSU1WvNdYS/dn9UxwsujGW4JX06dnHyjV2y9rRaybH0iQ=="], - - "bun-types": ["bun-types@1.3.3", "", { "dependencies": { "@types/node": "*" } }, "sha512-z3Xwlg7j2l9JY27x5Qn3Wlyos8YAp0kKRlrePAOjgjMGS5IG6E7Jnlx736vH9UVI4wUICwwhC9anYL++XeOgTQ=="], - - "mitt": ["mitt@3.0.1", "", {}, "sha512-vKivATfr97l2/QBCYAkXYDbrIWPM2IIKEl7YPhjCvKlG3kE2gm+uBo6nEXK3M5/Ffh/FLpKExzOQ3JJoJGFKBw=="], - - "react": ["react@19.2.0", "", {}, "sha512-tmbWg6W31tQLeB5cdIBOicJDJRR2KzXsV7uSK9iNfLWQ5bIZfxuPEHp7M8wiHyHnn0DD1i7w3Zmin0FtkrwoCQ=="], - - "react-dom": ["react-dom@19.2.0", "", { "dependencies": { "scheduler": "^0.27.0" }, "peerDependencies": { "react": "^19.2.0" } }, "sha512-UlbRu4cAiGaIewkPyiRGJk0imDN2T3JjieT6spoL2UeSf5od4n5LB/mQ4ejmxhCFT1tYe8IvaFulzynWovsEFQ=="], - - "regexparam": ["regexparam@3.0.0", "", {}, "sha512-RSYAtP31mvYLkAHrOlh25pCNQ5hWnT106VukGaaFfuJrZFkGRX5GhUAdPqpSDXxOhA2c4akmRuplv1mRqnBn6Q=="], - - "scheduler": ["scheduler@0.27.0", "", {}, "sha512-eNv+WrVbKu1f3vbYJT/xtiF5syA5HPIMtf9IgY/nKg0sWqzAUEvqY/xm7OcZc/qafLx/iO9FgOmeSAp4v5ti/Q=="], - - "typescript": ["typescript@5.9.3", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw=="], - - "undici-types": ["undici-types@7.16.0", "", {}, "sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw=="], - - "use-sync-external-store": ["use-sync-external-store@1.6.0", "", { "peerDependencies": { "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" } }, "sha512-Pp6GSwGP/NrPIrxVFAIkOQeyw8lFenOHijQWkUTrDvrF4ALqylP2C/KCkeS9dpUM3KvYRQhna5vt7IL95+ZQ9w=="], - - "wouter": ["wouter@3.8.0", "", { "dependencies": { "mitt": "^3.0.1", "regexparam": "^3.0.0", "use-sync-external-store": "^1.0.0" }, "peerDependencies": { "react": ">=16.8.0" } }, "sha512-oFQKbZVQxaXGXSuUg++FETTORSKQUur1Q+tvIlVm9TjPt+bpzS/IkRy2mXdSuqTjJ/PorSPxbRz4UwWsICes1Q=="], - } -} diff --git a/packages/magazin/cart.regression-1.test.tsx b/packages/magazin/cart.regression-1.test.tsx new file mode 100644 index 00000000..6c6982e1 --- /dev/null +++ b/packages/magazin/cart.regression-1.test.tsx @@ -0,0 +1,44 @@ +import { expect, test } from "bun:test"; +import { fireEvent, render } from "@testing-library/react"; +import { HelmetProvider } from "@dr.pogodin/react-helmet"; + +import { App } from "./App"; + +function renderAppAt(path: string) { + history.replaceState(null, "", path); + return render( + + + + ); +} + +// Regression: ISSUE-002 — Add to Cart showed success without changing the cart +// Found by /qa on 2026-09-05 +// Report: .gstack/qa-reports/qa-report-localhost-3002-2026-09-05.md +test("adds a new product and updates every cart summary", () => { + const { getByRole, getByText } = renderAppAt("/products/silver-ok-ring"); + + fireEvent.click(getByRole("link", { name: "Add to Cart" })); + + expect(location.pathname).toBe("/cart"); + expect(getByText("Silver OK Ring")).toBeTruthy(); + expect(getByText("1 × $99")).toBeTruthy(); + expect(getByRole("link", { name: "Shopping cart, 8 items" })).toBeTruthy(); + expect(getByText("$1,174")).toBeTruthy(); + expect(getByText("Silver OK Ring added to cart")).toBeTruthy(); +}); + +// Regression: ISSUE-003 — quantities were ignored by line and cart totals +// Found by /qa on 2026-09-05 +// Report: .gstack/qa-reports/qa-report-localhost-3002-2026-09-05.md +test("increments an existing product and calculates quantity-aware totals", () => { + const { getByRole, getByText } = renderAppAt("/products/hook-keyring-rvst"); + + fireEvent.click(getByRole("link", { name: "Add to Cart" })); + + expect(getByText("3 × $65")).toBeTruthy(); + expect(getByText("$195")).toBeTruthy(); + expect(getByText("$1,140")).toBeTruthy(); + expect(getByRole("link", { name: "Shopping cart, 8 items" })).toBeTruthy(); +}); diff --git a/packages/magazin/components/navbar.tsx b/packages/magazin/components/navbar.tsx index ff03835d..73935199 100644 --- a/packages/magazin/components/navbar.tsx +++ b/packages/magazin/components/navbar.tsx @@ -1,7 +1,12 @@ import { Link } from "wouter"; function Logo() { - return ; + return ( +