From f08f53fcd2fa0c37a26f75463d501005fe523c45 Mon Sep 17 00:00:00 2001 From: dangreen Date: Fri, 7 Aug 2026 18:34:14 +0400 Subject: [PATCH] feat(react): add React components --- packages/react/.size-limit.json | 8 + packages/react/README.md | 71 ++++++ packages/react/oxlint.config.ts | 18 ++ packages/react/package.json | 88 ++++++++ packages/react/src/Image.spec.tsx | 245 ++++++++++++++++++++ packages/react/src/Image.tsx | 138 ++++++++++++ packages/react/src/Picture.spec.tsx | 89 ++++++++ packages/react/src/Picture.tsx | 54 +++++ packages/react/src/index.ts | 2 + packages/react/test/setup.ts | 7 + packages/react/test/src.mock.ts | 41 ++++ packages/react/tsconfig.build.json | 20 ++ packages/react/tsconfig.json | 14 ++ packages/react/vite.config.js | 54 +++++ pnpm-lock.yaml | 333 +++++++++++++++++++++++++++- 15 files changed, 1179 insertions(+), 3 deletions(-) create mode 100644 packages/react/.size-limit.json create mode 100644 packages/react/README.md create mode 100644 packages/react/oxlint.config.ts create mode 100644 packages/react/package.json create mode 100644 packages/react/src/Image.spec.tsx create mode 100644 packages/react/src/Image.tsx create mode 100644 packages/react/src/Picture.spec.tsx create mode 100644 packages/react/src/Picture.tsx create mode 100644 packages/react/src/index.ts create mode 100644 packages/react/test/setup.ts create mode 100644 packages/react/test/src.mock.ts create mode 100644 packages/react/tsconfig.build.json create mode 100644 packages/react/tsconfig.json create mode 100644 packages/react/vite.config.js diff --git a/packages/react/.size-limit.json b/packages/react/.size-limit.json new file mode 100644 index 0000000..53adda0 --- /dev/null +++ b/packages/react/.size-limit.json @@ -0,0 +1,8 @@ +[ + { + "name": "All publics", + "path": "dist/index.js", + "import": "*", + "limit": "2 kB" + } +] diff --git a/packages/react/README.md b/packages/react/README.md new file mode 100644 index 0000000..739c5c3 --- /dev/null +++ b/packages/react/README.md @@ -0,0 +1,71 @@ +# @srcset/react + +[![ESM-only package][package]][package-url] +[![NPM version][npm]][npm-url] +[![Node version][node]][node-url] +[![Dependencies status][deps]][deps-url] +[![Install size][size]][size-url] +[![Build status][build]][build-url] +[![Coverage status][coverage]][coverage-url] + +[package]: https://img.shields.io/badge/package-ESM--only-ffe536.svg +[package-url]: https://nodejs.org/api/esm.html + +[npm]: https://img.shields.io/npm/v/%40srcset%2Freact.svg +[npm-url]: https://npmjs.com/package/@srcset/react + +[node]: https://img.shields.io/node/v/%40srcset%2Freact.svg +[node-url]: https://nodejs.org + +[deps]: https://img.shields.io/librariesio/release/npm/%40srcset%2Freact +[deps-url]: https://libraries.io/npm/%40srcset%2Freact + +[size]: https://deno.bundlejs.com/badge?q=%40srcset%2Freact +[size-url]: https://bundlejs.com/?q=%40srcset%2Freact + +[build]: https://img.shields.io/github/actions/workflow/status/TrigenSoftware/srcset/tests.yml?branch=main +[build-url]: https://github.com/TrigenSoftware/srcset/actions + +[coverage]: https://img.shields.io/codecov/c/github/TrigenSoftware/srcset.svg +[coverage-url]: https://app.codecov.io/gh/TrigenSoftware/srcset + +React components for responsive images: Picture and Image. + +- 🖼 `` sources grouped by mime type and ordered by format efficiency +- 🚀 Lazy loading and async decoding by default, `priority` for above-the-fold images +- 🌫 Blur-up placeholder background until the image loads +- 📐 Anti-CLS: intrinsic size from the image variant + +## Install + +```bash +# pnpm +pnpm add @srcset/react @srcset/runtime +# yarn +yarn add @srcset/react @srcset/runtime +# npm +npm i @srcset/react @srcset/runtime +``` + +## Usage + +```tsx +import { src, srcSet, placeholder } from './photo.jpg' +import { Picture, Image } from '@srcset/react' + +function Hero() { + return ( + + Hero photo + + ) +} +``` + +## Documentation + +For more details, guides and API references, check out the [documentation website](https://srcset.js.org/components/react/). diff --git a/packages/react/oxlint.config.ts b/packages/react/oxlint.config.ts new file mode 100644 index 0000000..231b124 --- /dev/null +++ b/packages/react/oxlint.config.ts @@ -0,0 +1,18 @@ +import { defineConfig } from '@trigen/oxlint' +import reactConfig from '@trigen/oxlint-config/react' +import testConfig from '@trigen/oxlint-config/test' +import tsTypeCheckedConfig from '@trigen/oxlint-config/typescript-type-checked' +import rootConfig from '../../oxlint.config.ts' + +export default defineConfig({ + extends: [ + rootConfig, + reactConfig, + tsTypeCheckedConfig, + testConfig + ], + env: { + node: false, + browser: true + } +}) diff --git a/packages/react/package.json b/packages/react/package.json new file mode 100644 index 0000000..335cc82 --- /dev/null +++ b/packages/react/package.json @@ -0,0 +1,88 @@ +{ + "name": "@srcset/react", + "type": "module", + "version": "1.0.0", + "description": "React components for responsive images: Picture and Image.", + "author": { + "name": "Dan Onoshko", + "email": "danon0404@gmail.com", + "url": "https://github.com/dangreen" + }, + "license": "MIT", + "homepage": "https://srcset.js.org/components/react/", + "funding": "https://ko-fi.com/dangreen", + "repository": { + "type": "git", + "url": "https://github.com/TrigenSoftware/srcset.git", + "directory": "packages/react" + }, + "bugs": { + "url": "https://github.com/TrigenSoftware/srcset/issues" + }, + "keywords": [ + "srcset", + "image", + "picture", + "responsive", + "react", + "components" + ], + "engines": { + "node": ">=22" + }, + "sideEffects": false, + "exports": { + "./package.json": "./package.json", + ".": "./src/index.ts" + }, + "publishConfig": { + "exports": { + "./package.json": "./package.json", + ".": { + "types": "./dist/index.d.ts", + "default": "./dist/index.js" + } + }, + "directory": "package", + "linkDirectory": false + }, + "files": [ + "dist" + ], + "scripts": { + "clear:package": "del ./package", + "clear:dist": "del ./dist", + "clear": "del ./package ./dist ./coverage", + "prepublishOnly": "run build clear:package clean-publish", + "postpublish": "pnpm clear:package", + "emitDeclarations": "tsc -p ./tsconfig.build.json --emitDeclarationOnly", + "build:dist": "run -p emitDeclarations [ vite build ]", + "build": "run clear:dist build:dist", + "lint": "oxlint", + "format": "oxlint --fix", + "test:unit": "vitest run --coverage", + "test:unit:watch": "vitest watch", + "test:size": "size-limit", + "test:types": "tsc --noEmit", + "test": "run -p lint test:unit test:types" + }, + "peerDependencies": { + "@srcset/runtime": "workspace:^", + "react": ">=19", + "react-dom": ">=19" + }, + "devDependencies": { + "@size-limit/preset-small-lib": "^12.0.0", + "@srcset/runtime": "workspace:^", + "@testing-library/dom": "^10.0.0", + "@testing-library/jest-dom": "^6.6.3", + "@testing-library/react": "^16.1.0", + "@types/react": "^19.2.4", + "@types/react-dom": "^19.2.3", + "@vitejs/plugin-react": "^6.0.1", + "happy-dom": "^20.0.0", + "react": "^19.2.0", + "react-dom": "^19.2.0", + "size-limit": "^12.0.0" + } +} diff --git a/packages/react/src/Image.spec.tsx b/packages/react/src/Image.spec.tsx new file mode 100644 index 0000000..cc2ac8c --- /dev/null +++ b/packages/react/src/Image.spec.tsx @@ -0,0 +1,245 @@ +import { + describe, + it, + expect, + vi +} from 'vitest' +import { + render, + screen, + fireEvent +} from '@testing-library/react' +import { createRef } from 'react' +import type * as ReactDomModule from 'react-dom' +import { preload } from 'react-dom' +import { Image } from './Image.tsx' +import { + createSrc, + createSrcSet +} from '../test/src.mock.ts' + +vi.mock('react-dom', async (importOriginal) => { + const original = await importOriginal() + + return { + ...original, + preload: vi.fn() + } +}) + +describe('react', () => { + describe('Image', () => { + it('should render img from variant with intrinsic size', () => { + render( + photo + ) + + const image = screen.getByAltText('photo') + + expect(image).toHaveAttribute('src', '/images/image@640w.jpg') + expect(image).toHaveAttribute('width', '640') + expect(image).toHaveAttribute('height', '480') + }) + + it('should build srcset from variants of src format', () => { + render( + photo + ) + + expect(screen.getByAltText('photo')).toHaveAttribute( + 'srcset', + '/images/image@320w.jpg 320w, /images/image@640w.jpg 640w' + ) + }) + + it('should accept srcset string as is', () => { + render( + photo + ) + + expect(screen.getByAltText('photo')).toHaveAttribute('srcset', 'custom 2x') + }) + + it('should override intrinsic size with props', () => { + render( + photo + ) + + const image = screen.getByAltText('photo') + + expect(image).toHaveAttribute('width', '100') + expect(image).toHaveAttribute('height', '75') + }) + + it('should not fill the other dimension for a single override', () => { + render( + photo + ) + + const image = screen.getByAltText('photo') + + expect(image).toHaveAttribute('width', '100') + expect(image).not.toHaveAttribute('height') + }) + + it('should be lazy with async decoding by default', () => { + render( + photo + ) + + const image = screen.getByAltText('photo') + + expect(image).toHaveAttribute('loading', 'lazy') + expect(image).toHaveAttribute('decoding', 'async') + }) + + it('should load eagerly with high priority and preload', () => { + render( + photo + ) + + const image = screen.getByAltText('photo') + + expect(image).toHaveAttribute('loading', 'eager') + expect(image).toHaveAttribute('fetchpriority', 'high') + expect(preload).toHaveBeenCalledWith('/images/image@640w.jpg', { + as: 'image', + type: 'image/jpeg', + fetchPriority: 'high', + imageSrcSet: '/images/image@320w.jpg 320w, /images/image@640w.jpg 640w', + imageSizes: '100vw', + crossOrigin: 'anonymous', + referrerPolicy: 'no-referrer' + }) + }) + + it('should show placeholder background until load', () => { + render( + photo + ) + + const image = screen.getByAltText('photo') + + expect(image).toHaveAttribute('data-loading') + expect(image.style.backgroundImage).toBe('url("data:image/webp;base64,abc")') + + fireEvent.load(image) + + expect(image).not.toHaveAttribute('data-loading') + expect(image.style.backgroundImage).toBe('') + }) + + it('should drop placeholder for an image completed before hydration', () => { + const complete = vi.spyOn(window.HTMLImageElement.prototype, 'complete', 'get').mockReturnValue(true) + const naturalWidth = vi.spyOn(window.HTMLImageElement.prototype, 'naturalWidth', 'get').mockReturnValue(640) + + try { + render( + photo + ) + + const image = screen.getByAltText('photo') + + expect(image).not.toHaveAttribute('data-loading') + expect(image.style.backgroundImage).toBe('') + } finally { + complete.mockRestore() + naturalWidth.mockRestore() + } + }) + + it('should run callback ref cleanup on unmount', () => { + const cleanup = vi.fn() + const refCallback = vi.fn(() => cleanup) + const { unmount } = render( + photo + ) + + expect(refCallback).toHaveBeenCalledOnce() + + unmount() + + expect(cleanup).toHaveBeenCalledOnce() + // With a cleanup returned, React must not call the ref with `null`. + expect(refCallback).toHaveBeenCalledOnce() + }) + + it('should call onLoad prop', () => { + const onLoad = vi.fn() + + render( + photo + ) + + fireEvent.load(screen.getByAltText('photo')) + + expect(onLoad).toHaveBeenCalledOnce() + }) + + it('should pass class and ref through', () => { + const ref = createRef() + + render( + photo + ) + + expect(screen.getByAltText('photo')).toHaveClass('hero') + expect(ref.current).toBeInstanceOf(HTMLImageElement) + }) + }) +}) diff --git a/packages/react/src/Image.tsx b/packages/react/src/Image.tsx new file mode 100644 index 0000000..2f2ed53 --- /dev/null +++ b/packages/react/src/Image.tsx @@ -0,0 +1,138 @@ +'use client' +import { + type ComponentProps, + type ReactEventHandler, + type Ref, + type RefCallback, + useCallback, + useState +} from 'react' +import { preload } from 'react-dom' +import { + type SrcSetEntry, + getImageProps +} from '@srcset/runtime' + +function applyRef(ref: Ref | undefined, node: T | null) { + if (typeof ref === 'function') { + // React 19 callback refs may return a cleanup function: pass it through. + return ref(node) + } + + if (ref) { + ref.current = node + } + + return undefined +} + +export interface ImageProps extends Omit, 'src' | 'srcSet'> { + /** + * Image variant for the `src` attribute and intrinsic size. + */ + src?: SrcSetEntry + /** + * Variants for the `srcset` attribute: array to build a width descriptors string + * from the variants of the `src` format, or a ready `srcset` string. + */ + srcSet?: SrcSetEntry[] | string + /** + * Data-url shown as a background until the image loads. + * Shown once per mount: remount with a `key` for a new image source. + */ + placeholder?: string + /** + * Load the image eagerly with high priority and preload it. + * Do not use inside `Picture`: the preload would request the fallback + * format, while the browser picks one of the `source` elements. + */ + priority?: boolean +} + +/** + * `` element from image variants: intrinsic size, lazy loading + * and async decoding by default, optional blur-up placeholder. + * @param props - Image props. + */ +export function Image({ + src, + srcSet, + placeholder, + priority, + width, + height, + loading, + decoding, + fetchPriority, + style, + onLoad, + ref, + ...props +}: ImageProps) { + const [loaded, setLoaded] = useState(false) + const imageProps = getImageProps(src, srcSet) + const showPlaceholder = Boolean(placeholder) && !loaded + const onLoadCallback = useCallback>( + (event) => { + setLoaded(true) + onLoad?.(event) + }, + [onLoad] + ) + // The load event may have fired before hydration for cached images. + const refCallback = useCallback>( + (node) => { + if (node?.complete && node.naturalWidth > 0) { + setLoaded(true) + } + + return applyRef(ref, node) + }, + [ref] + ) + let finalWidth = width + let finalHeight = height + + // The intrinsic pair applies only as a whole: with a single explicit + // dimension, filling the other from the variant would skew the ratio. + if (width === undefined && height === undefined && src) { + finalWidth = src.width + finalHeight = src.height + } + + if (priority && imageProps.src) { + // The request policy must match the image request, + // otherwise the preloaded response is not reusable. + preload(imageProps.src, { + as: 'image', + type: src?.type, + fetchPriority: 'high', + imageSrcSet: imageProps.srcSet, + imageSizes: props.sizes, + crossOrigin: props.crossOrigin, + referrerPolicy: props.referrerPolicy + }) + } + + return ( + + ) +} diff --git a/packages/react/src/Picture.spec.tsx b/packages/react/src/Picture.spec.tsx new file mode 100644 index 0000000..f5003f6 --- /dev/null +++ b/packages/react/src/Picture.spec.tsx @@ -0,0 +1,89 @@ +import { + describe, + it, + expect +} from 'vitest' +import { + render, + screen +} from '@testing-library/react' +import { Picture } from './Picture.tsx' +import { Image } from './Image.tsx' +import { + createSrc, + createSrcSet +} from '../test/src.mock.ts' + +const srcSet = [ + ...createSrcSet('jpg', [320, 640]), + ...createSrcSet('webp', [320, 640]), + ...createSrcSet('avif', [320, 640]) +] + +describe('react', () => { + describe('Picture', () => { + it('should render sources ordered by format efficiency', () => { + const { container } = render( + + photo + + ) + const sources = [...container.querySelectorAll('source')] + + expect(sources.map(source => source.type)).toEqual([ + 'image/avif', + 'image/webp', + 'image/jpeg' + ]) + expect(sources[0]).toHaveAttribute( + 'srcset', + '/images/image@320w.avif 320w, /images/image@640w.avif 640w' + ) + }) + + it('should apply sizes to every source', () => { + const { container } = render( + + photo + + ) + const sources = [...container.querySelectorAll('source')] + + expect(sources.length).toBeGreaterThan(0) + sources.forEach((source) => { + expect(source).toHaveAttribute('sizes', '(max-width: 600px) 100vw, 50vw') + }) + }) + + it('should render img child inside picture', () => { + const { container } = render( + + photo + + ) + const image = screen.getByAltText('photo') + + expect(image.closest('picture')).toBe(container.querySelector('picture')) + expect(image).toHaveAttribute('src', '/images/image@640w.jpg') + }) + + it('should pass picture attributes through', () => { + const { container } = render( + + photo + + ) + + expect(container.querySelector('picture')).toHaveClass('hero') + }) + }) +}) diff --git a/packages/react/src/Picture.tsx b/packages/react/src/Picture.tsx new file mode 100644 index 0000000..3ac274a --- /dev/null +++ b/packages/react/src/Picture.tsx @@ -0,0 +1,54 @@ +import type { + ComponentProps, + ReactNode +} from 'react' +import { + type SrcSetEntry, + getSourceProps +} from '@srcset/runtime' + +export interface PictureProps extends ComponentProps<'picture'> { + /** + * Variants to render `` elements from, grouped by mime type + * and ordered by format efficiency. + */ + srcSet: SrcSetEntry[] + /** + * `sizes` for the generated `` elements: the `sizes` of the + * child `` does not apply to a selected ``. + * Should match the image `sizes`. + */ + sizes?: string + /** + * `` element, required: `` renders nothing without it. + */ + children: ReactNode +} + +/** + * `` element with `` per format group of the variants, + * ordered by format efficiency (avif, webp, then the rest). + * Children must contain an `` element, e.g. the `Image` component: + * `` renders nothing without it. + * @param props - Picture props. + */ +export function Picture({ + srcSet, + sizes, + children, + ...props +}: PictureProps) { + return ( + + {getSourceProps(srcSet).map(source => ( + + ))} + {children} + + ) +} diff --git a/packages/react/src/index.ts b/packages/react/src/index.ts new file mode 100644 index 0000000..a2496da --- /dev/null +++ b/packages/react/src/index.ts @@ -0,0 +1,2 @@ +export * from './Image.tsx' +export * from './Picture.tsx' diff --git a/packages/react/test/setup.ts b/packages/react/test/setup.ts new file mode 100644 index 0000000..2287bd5 --- /dev/null +++ b/packages/react/test/setup.ts @@ -0,0 +1,7 @@ +import '@testing-library/jest-dom/vitest' +import { afterEach } from 'vitest' +import { cleanup } from '@testing-library/react' + +afterEach(() => { + cleanup() +}) diff --git a/packages/react/test/src.mock.ts b/packages/react/test/src.mock.ts new file mode 100644 index 0000000..f8baa09 --- /dev/null +++ b/packages/react/test/src.mock.ts @@ -0,0 +1,41 @@ +import type { + SrcSetEntry, + ImageFormat +} from '@srcset/runtime' + +const mimeTypes: Record = { + avif: 'image/avif', + webp: 'image/webp', + jpg: 'image/jpeg', + png: 'image/png', + gif: 'image/gif', + svg: 'image/svg+xml' +} + +/** + * Create an image variant stub. + * @param format - Image format. + * @param width - Image width. + * @param height - Image height. + * @returns Image variant. + */ +export function createSrc(format: ImageFormat, width: number, height = Math.round(width * 0.75)): SrcSetEntry { + return { + id: `${format}${width}`, + format, + type: mimeTypes[format], + width, + height, + url: `/images/image@${width}w.${format}` + } +} + +/** + * Create a set of image variant stubs. + * @param format - Image format. + * @param widths - Image widths. + * @returns Set of image variants. + */ +export function createSrcSet(format: ImageFormat, widths: number[]): SrcSetEntry[] { + return widths.map(width => createSrc(format, width)) +} diff --git a/packages/react/tsconfig.build.json b/packages/react/tsconfig.build.json new file mode 100644 index 0000000..50b42fa --- /dev/null +++ b/packages/react/tsconfig.build.json @@ -0,0 +1,20 @@ +{ + "extends": "../../tsconfig.json", + "compilerOptions": { + "rootDir": "./src", + "outDir": "dist", + "jsx": "react-jsx", + "lib": [ + "esnext", + "dom" + ] + }, + "include": [ + "src" + ], + "exclude": [ + "**/*.config.ts", + "**/*.spec.ts", + "**/*.spec.tsx" + ] +} diff --git a/packages/react/tsconfig.json b/packages/react/tsconfig.json new file mode 100644 index 0000000..2783c96 --- /dev/null +++ b/packages/react/tsconfig.json @@ -0,0 +1,14 @@ +{ + "extends": "./tsconfig.build.json", + "compilerOptions": { + "rootDir": "../../", + "allowJs": true + }, + "include": [ + "src", + "test", + "*.js", + "*.ts" + ], + "exclude": [] +} diff --git a/packages/react/vite.config.js b/packages/react/vite.config.js new file mode 100644 index 0000000..c14690b --- /dev/null +++ b/packages/react/vite.config.js @@ -0,0 +1,54 @@ +import { defineConfig } from 'vite' +import { configDefaults } from 'vitest/config' +import react from '@vitejs/plugin-react' + +export default defineConfig({ + plugins: [react()], + build: { + target: 'esnext', + lib: { + formats: ['es'], + entry: { + index: './src/index.ts' + } + }, + rolldownOptions: { + external: id => /^(react|@srcset)\/?/.test(id), + output: { + topLevelVar: false, + codeSplitting: { + groups: [ + { + name(id, ctx) { + const { code } = ctx.getModuleInfo(id) + + if (/^\s*['"]use client['"]/.test(code)) { + return 'client' + } + + return null + } + } + ] + }, + banner(chunk) { + if (chunk.name === 'client') { + return `'use client';` + } + } + } + }, + sourcemap: true, + minify: false, + emptyOutDir: false + }, + test: { + environment: 'happy-dom', + setupFiles: ['./test/setup.ts'], + exclude: [...configDefaults.exclude, './package'], + coverage: { + reporter: ['lcovonly', 'text'], + include: ['src/**/*'] + } + } +}) diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 4f88029..11c3131 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -66,7 +66,7 @@ importers: version: 8.1.5(@types/node@22.20.1)(esbuild@0.28.1)(jiti@2.6.1)(terser@5.49.0) vitest: specifier: ^4.1.9 - version: 4.1.10(@types/node@22.20.1)(@vitest/coverage-v8@4.1.10)(vite@8.1.5(@types/node@22.20.1)(esbuild@0.28.1)(jiti@2.6.1)(terser@5.49.0)) + version: 4.1.10(@types/node@22.20.1)(@vitest/coverage-v8@4.1.10)(happy-dom@20.11.1)(vite@8.1.5(@types/node@22.20.1)(esbuild@0.28.1)(jiti@2.6.1)(terser@5.49.0)) packages/bundler-utils: dependencies: @@ -163,6 +163,46 @@ importers: version: 5.108.4(esbuild@0.28.1) publishDirectory: package + packages/react: + devDependencies: + '@size-limit/preset-small-lib': + specifier: ^12.0.0 + version: 12.1.0(size-limit@12.1.0(jiti@2.6.1)) + '@srcset/runtime': + specifier: workspace:^ + version: link:../runtime + '@testing-library/dom': + specifier: ^10.0.0 + version: 10.4.1 + '@testing-library/jest-dom': + specifier: ^6.6.3 + version: 6.10.0(@testing-library/dom@10.4.1) + '@testing-library/react': + specifier: ^16.1.0 + version: 16.3.2(@testing-library/dom@10.4.1)(@types/react-dom@19.2.4(@types/react@19.2.18))(@types/react@19.2.18)(react-dom@19.2.8(react@19.2.8))(react@19.2.8) + '@types/react': + specifier: ^19.2.4 + version: 19.2.18 + '@types/react-dom': + specifier: ^19.2.3 + version: 19.2.4(@types/react@19.2.18) + '@vitejs/plugin-react': + specifier: ^6.0.1 + version: 6.0.5(vite@8.1.5(@types/node@22.20.1)(esbuild@0.28.1)(jiti@2.6.1)(terser@5.49.0)) + happy-dom: + specifier: ^20.0.0 + version: 20.11.1 + react: + specifier: ^19.2.0 + version: 19.2.8 + react-dom: + specifier: ^19.2.0 + version: 19.2.8(react@19.2.8) + size-limit: + specifier: ^12.0.0 + version: 12.1.0(jiti@2.6.1) + publishDirectory: package + packages/runtime: devDependencies: '@size-limit/preset-small-lib': @@ -201,6 +241,9 @@ importers: packages: + '@adobe/css-tools@4.5.0': + resolution: {integrity: sha512-6OzddxPio9UiWTCemp4N8cYLV2ZN1ncRnV1cVGtve7dhPOtRkleRyx32GQCYSwDYgaHU3USMm84tNsvKzRCa1Q==} + '@babel/code-frame@7.29.7': resolution: {integrity: sha512-Aup7aUOfpbAUg2ROOJN6Iw5f9DMBlzu0mIkm/malLQFN/YQgO48wCj0Kxa3sEHJvPVFg7siR+qRInwXd2qhQKw==} engines: {node: '>=6.9.0'} @@ -218,6 +261,10 @@ packages: engines: {node: '>=6.0.0'} hasBin: true + '@babel/runtime@7.29.7': + resolution: {integrity: sha512-Nq8OhGWiZIZGV6hLHoyAKLLcJihP/xFeBMGJoUrxTX2psI8dCifzLhZISFb+VWS3wFMRDmCGw5R+dOySCqPLhw==} + engines: {node: '>=6.9.0'} + '@babel/types@7.29.7': resolution: {integrity: sha512-4zBIxpPzowiZpusoFkyGVwakdRJUyuH5PxQ/PrqghfdFWWasvnCdPfQXHrenDai+gyLARulZjZowCOj6fjT4pA==} engines: {node: '>=6.9.0'} @@ -1227,6 +1274,32 @@ packages: peerDependencies: eslint: ^9.0.0 || ^10.0.0 + '@testing-library/dom@10.4.1': + resolution: {integrity: sha512-o4PXJQidqJl82ckFaXUeoAW+XysPLauYI43Abki5hABd853iMhitooc6znOnczgbTYmEP6U6/y1ZyKAIsvMKGg==} + engines: {node: '>=18'} + + '@testing-library/jest-dom@6.10.0': + resolution: {integrity: sha512-HQwu0KaB2zyT0iLzBL+8CLyZDL3KlZlZJ+2iyc9uCUnlJVskJU/UlPuVCyIPhtukjPQdT2QNoR5nCP5FqTmmDQ==} + engines: {node: '>=22', npm: '>=6', yarn: '>=1'} + deprecated: Incorrect minor release with breaking changes (Node >=22 and required @testing-library/dom peer). Use 6.9.1 for the 6.x line, or upgrade to 7.0.0. + peerDependencies: + '@testing-library/dom': '>=10 <11' + + '@testing-library/react@16.3.2': + resolution: {integrity: sha512-XU5/SytQM+ykqMnAnvB2umaJNIOsLF3PVv//1Ew4CTcpz0/BRyy/af40qqrt7SjKpDdT1saBMc42CUok5gaw+g==} + engines: {node: '>=18'} + peerDependencies: + '@testing-library/dom': ^10.0.0 + '@types/react': ^18.0.0 || ^19.0.0 + '@types/react-dom': ^18.0.0 || ^19.0.0 + react: ^18.0.0 || ^19.0.0 + react-dom: ^18.0.0 || ^19.0.0 + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + '@trigen/oxlint-config@10.1.2': resolution: {integrity: sha512-v5RsINtl8dYo7VJnfL0FiP13ITtWuY66Po89WX8N3+u1LkBGc9l5i64ZJUnfYP59zY3l3067cPJ7s+Feas7UzA==} engines: {node: '>=22'} @@ -1249,6 +1322,9 @@ packages: '@tybys/wasm-util@0.10.3': resolution: {integrity: sha512-F3fo1MYrRJYL3zER0OUOmkutjr1Vp23m7OsSgp7nq4SP6OqX6C/56XFIPAl5bt3zaBRjmW7SGz3u/6LwFpYcOg==} + '@types/aria-query@5.0.4': + resolution: {integrity: sha512-rfT93uj5s0PRL7EzccGMs3brplhcrghnDoV26NqKhCAS1hVo+WdNsPvE/yb6ilfr5hi2MEk6d5EWJTKdxg8jVw==} + '@types/chai@5.2.3': resolution: {integrity: sha512-Mw558oeA9fFbv65/y4mHtXDs9bPnFMZAL/jxdPFUpOHHIXX91mcgEHbS5Lahr+pwZFR8A7GQleRWeI6cGFC2UA==} @@ -1270,6 +1346,20 @@ packages: '@types/picomatch@4.0.3': resolution: {integrity: sha512-iG0T6+nYJ9FAPmx9SsUlnwcq1ZVRuCXcVEvWnntoPlrOpwtSTKNDC9uVAxTsC3PUvJ+99n4RpAcNgBbHX3JSnQ==} + '@types/react-dom@19.2.4': + resolution: {integrity: sha512-Bsc+QHgp+P/F02XDzNCY9jnZNCUuLki36KT7VKrTXXLdHf+vHMNZnW1rVu5DNW/rCK+fya3DATySbLM4yhtKUw==} + peerDependencies: + '@types/react': ^19.2.0 + + '@types/react@19.2.18': + resolution: {integrity: sha512-AnzbBERsrLKtk2XSfTbYRLjQPdy116Sty4q+T+Bp3IC4l6jNBvreVPAHmpq9qhXQM7CXZPjLVmGMw9sy+hxQ3w==} + + '@types/whatwg-mimetype@3.0.2': + resolution: {integrity: sha512-c2AKvDT8ToxLIOUlN51gTiHXflsfIFisS4pO7pDPoKouJCESkhZnEy623gwP9laCy5lnLDAw1vAzu2vM2YLOrA==} + + '@types/ws@8.18.1': + resolution: {integrity: sha512-ThVF6DCVhA8kUGy+aazFQ4kXQ7E1Ty7A3ypFOe0IcJV8O/M511G99AW24irKrW56Wt44yG9+ij8FaqoBGkuBXg==} + '@typescript-eslint/types@8.64.0': resolution: {integrity: sha512-qjhfuTfLXjA4IOzXvz0rTjT01BqEiIgPoUeMwiEjnaHKJMTNo8rH5pYW1a2L/0Dnux2fPC85AeyJoWaGa8WxTA==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} @@ -1394,6 +1484,19 @@ packages: cpu: [x64] os: [win32] + '@vitejs/plugin-react@6.0.5': + resolution: {integrity: sha512-BOVzne/NL162sMdResB25mUv+vWMF5NoAjNf09TeGlE7ZpszZWSD3winycicLJw72yeVsoCn/2kOhEuCvEShMA==} + engines: {node: ^20.19.0 || >=22.12.0} + peerDependencies: + '@rolldown/plugin-babel': ^0.1.7 || ^0.2.0 + babel-plugin-react-compiler: ^1.0.0 + vite: ^8.0.0 + peerDependenciesMeta: + '@rolldown/plugin-babel': + optional: true + babel-plugin-react-compiler: + optional: true + '@vitest/coverage-v8@4.1.10': resolution: {integrity: sha512-IM49HmthevbgAO4anp1hwtoT9wYe59w0LR00gr+eagHE+ZJ5lK4sLPeO0ubgoJcwLk6dehU3R24N+FbEEKDc8g==} peerDependencies: @@ -1530,6 +1633,10 @@ packages: resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==} engines: {node: '>=8'} + ansi-styles@5.2.0: + resolution: {integrity: sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==} + engines: {node: '>=10'} + ansi-styles@6.2.3: resolution: {integrity: sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==} engines: {node: '>=12'} @@ -1541,6 +1648,13 @@ packages: resolution: {integrity: sha512-DhBpBfXL4SS2uC0N922MMajKR3CdrTG0u2or1PNYgXMsrSzViJrbtvT0nCLlLGUI0plam/ZZCs7aAauHtW9thw==} engines: {node: '>=22'} + aria-query@5.3.0: + resolution: {integrity: sha512-b0P0sZPKtyu8HkeRAfCq0IfURZK+SuwMjY1UXGBU27wpAiTwQAIlq56IbIO+ytk/JjS1fMR14ee5WBBfKi5J6A==} + + aria-query@5.3.2: + resolution: {integrity: sha512-COROpnaoap1E2F000S62r6A60uHZnmlvomhfyT2DlTcrY1OrBKn2UhH7qn5wTC9zMvD0AY7csdPSNwKP+7WiQw==} + engines: {node: '>= 0.4'} + assertion-error@2.0.1: resolution: {integrity: sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==} engines: {node: '>=12'} @@ -1581,6 +1695,10 @@ packages: buffer-from@1.1.2: resolution: {integrity: sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==} + buffer-image-size@0.6.4: + resolution: {integrity: sha512-nEh+kZOPY1w+gcCMobZ6ETUp9WfibndnosbpwB1iJk/8Gt5ZF2bhS6+B6bPYz424KtwsR6Rflc3tCz1/ghX2dQ==} + engines: {node: '>=4.0'} + buffer@5.7.1: resolution: {integrity: sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==} @@ -1710,6 +1828,12 @@ packages: css-mediaquery@0.1.2: resolution: {integrity: sha512-COtn4EROW5dBGlE/4PiKnh6rZpAPxDeFLaEEwt4i10jpDMFt2EhQGS79QmmrO+iKCHv0PU/HrOWEhijFd1x99Q==} + css.escape@1.5.1: + resolution: {integrity: sha512-YUifsXXuknHlUsmlgyY0PKzgPOr7/FjCePfHNt0jxm83wHZi44VDMQ7/fGNkjY3/jV1MC+1CmZbaHzugyeRtpg==} + + csstype@3.2.3: + resolution: {integrity: sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==} + cz-conventional-changelog@3.3.0: resolution: {integrity: sha512-U466fIzU5U22eES5lTNiNbZ+d8dfcHcssH4o7QsdWaCcRs/feIPCxKYSWkYBNs5mny7MvEfwpTLWjvbm94hecw==} engines: {node: '>= 10'} @@ -1729,6 +1853,10 @@ packages: resolution: {integrity: sha512-gPqh0mKTPvaUZGAuHbrBUYKZWBNAeHG7TU3QH5EhVwPMyKvmfJaNXhcD2jTcXsJRRcffuho4vaYweu80dRrMGA==} engines: {node: '>=18'} + dequal@2.0.3: + resolution: {integrity: sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==} + engines: {node: '>=6'} + detect-file@1.0.0: resolution: {integrity: sha512-DtCOLG98P007x7wiiOmfI0fi3eIKyWiLTGJ2MDnVi/E04lWGbf+JzrRHMm0rgIIZJGtHpKpbVgLWHrv8xXpc3Q==} engines: {node: '>=0.10.0'} @@ -1741,6 +1869,12 @@ packages: resolution: {integrity: sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==} engines: {node: '>=8'} + dom-accessibility-api@0.5.16: + resolution: {integrity: sha512-X7BJ2yElsnOJ30pZF4uIIDfBEVgF4XEBxL9Bxhy6dnrm5hkzqmsWHGTiHqRiITNhMyFLyAiWndIJP7Z1NTteDg==} + + dom-accessibility-api@0.6.3: + resolution: {integrity: sha512-7ZgogeTnjuHbo+ct10G9Ffp0mif17idi0IyWNVA/wcwcm7NPOD/WEHVP3n7n3MhXqxoIYm8d6MuZohYWIZ4T3w==} + electron-to-chromium@1.5.393: resolution: {integrity: sha512-kiDJdIUawuEIcp9XoICKp1iTYDEbgguIPq526N1Q7jIQDeQ3CqoMx71025PI/7E48Ddtw2HuWsVjY7afEgNxmg==} @@ -1754,6 +1888,10 @@ packages: resolution: {integrity: sha512-PwKooW9JUzh5chmYfHM3IQl5OkK2u2Nm011MgeZrss3JmFraUx/fqrf78kk8GUMYoibx/14MdwTl/1WKkG7TpQ==} engines: {node: '>=10.13.0'} + entities@7.0.1: + resolution: {integrity: sha512-TWrgLOFUQTH994YUyl1yT4uyavY5nNB5muff+RtWaqNVCAK408b5ZnnbNAUEWLTCpum9w6arT70i1XdQ4UeOPA==} + engines: {node: '>=0.12'} + env-paths@2.2.1: resolution: {integrity: sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A==} engines: {node: '>=6'} @@ -1916,6 +2054,10 @@ packages: graceful-fs@4.2.11: resolution: {integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==} + happy-dom@20.11.1: + resolution: {integrity: sha512-XSt8tMzbW9ymE7687xztkO1ckR7qJNQ3LywY9vlYGhGi3zXrGBHuUo2Cl1ztZaICW+1eAGdkLbj6iwVqDT33kg==} + engines: {node: '>=20.0.0'} + has-flag@3.0.0: resolution: {integrity: sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==} engines: {node: '>=4'} @@ -1954,6 +2096,10 @@ packages: resolution: {integrity: sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==} engines: {node: '>=0.8.19'} + indent-string@4.0.0: + resolution: {integrity: sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==} + engines: {node: '>=8'} + inflight@1.0.6: resolution: {integrity: sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==} deprecated: This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful. @@ -2171,6 +2317,10 @@ packages: resolution: {integrity: sha512-Ajzxb8CM6WAnFjgiloPsI3bF+WCxcvhdIG3KNA2KN962+tdBsHcuQ4k4qX/EcS/2CRkcc0iAkR956Nib6aXU/Q==} engines: {node: '>=0.10.0'} + lz-string@1.5.0: + resolution: {integrity: sha512-h5bgJWpxJNswbU7qCrV0tIKQCaS3blPDrqKWx+QxzuzL1zGUzij9XCWLrSLsJPu5t+eWA/ycetzYAO5IOMcWAQ==} + hasBin: true + magic-string@0.30.21: resolution: {integrity: sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==} @@ -2210,6 +2360,10 @@ packages: resolution: {integrity: sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==} engines: {node: '>=6'} + min-indent@1.0.1: + resolution: {integrity: sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg==} + engines: {node: '>=4'} + minimatch@3.1.5: resolution: {integrity: sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==} @@ -2373,9 +2527,25 @@ packages: resolution: {integrity: sha512-E6rsNU1QNJgB3sjj7OANinGncFKuK+164sLXw1/CqBjj/EkXSoSdHCtWQGBNlREIGLnL7IEUEGa08YFVUbrhVg==} engines: {node: '>=16'} + pretty-format@27.5.1: + resolution: {integrity: sha512-Qb1gy5OrP5+zDf2Bvnzdl3jsTf1qXVMazbvCoKhtKqVs4/YK4ozX4gKQJJVyNe+cajNPn0KoC0MC3FUmaHWEmQ==} + engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0} + queue-microtask@1.2.3: resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==} + react-dom@19.2.8: + resolution: {integrity: sha512-rVprimfGBG3DR+Tq0IQG2DT5PxKth1WIGDmj5yPmlzr4YBe7uyE+Du4oVqTDXZSHGGGXRtTJEGSSePyQCMBglQ==} + peerDependencies: + react: ^19.2.8 + + react-is@17.0.2: + resolution: {integrity: sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==} + + react@19.2.8: + resolution: {integrity: sha512-PWaYA1L/q9u2u7xYQi+Y3L3Yfnie7XyLeaJICV1MGD6LprsBxcAqGjYyr0eY3p+QdsA+x/Irkt4Qif8D63+Sbw==} + engines: {node: '>=0.10.0'} + read-yaml-file@2.1.0: resolution: {integrity: sha512-UkRNRIwnhG+y7hpqnycCL/xbTk7+ia9VuVTC0S+zVbwd65DI9eUpRMfsWIGrCWxTU/mi+JW8cHQCrv+zfCbEPQ==} engines: {node: '>=10.13'} @@ -2388,6 +2558,10 @@ packages: resolution: {integrity: sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==} engines: {node: '>= 6'} + redent@3.0.0: + resolution: {integrity: sha512-6tDA8g98We0zd0GvVeMT9arEOnTw9qM03L9cJXaCjrip1OO764RDBLBfrB4cwzNGDj5OA5ioymC9GkizgWJDUg==} + engines: {node: '>=8'} + require-from-string@2.0.2: resolution: {integrity: sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==} engines: {node: '>=0.10.0'} @@ -2433,6 +2607,9 @@ packages: safer-buffer@2.1.2: resolution: {integrity: sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==} + scheduler@0.27.0: + resolution: {integrity: sha512-eNv+WrVbKu1f3vbYJT/xtiF5syA5HPIMtf9IgY/nKg0sWqzAUEvqY/xm7OcZc/qafLx/iO9FgOmeSAp4v5ti/Q==} + schema-utils@4.3.3: resolution: {integrity: sha512-eflK8wEtyOE6+hsaRVPxvUKYCpRgzLqDTb8krvAsRIwOGlHoSgYLgBXoubGgLd2fT41/OUYdb48v4k4WWHQurA==} engines: {node: '>= 10.13.0'} @@ -2538,6 +2715,10 @@ packages: strip-comments-strings@1.2.0: resolution: {integrity: sha512-zwF4bmnyEjZwRhaak9jUWNxc0DoeKBJ7lwSN/LEc8dQXZcUFG6auaaTQJokQWXopLdM3iTx01nQT8E4aL29DAQ==} + strip-indent@3.0.0: + resolution: {integrity: sha512-laJTa3Jb+VQpaC6DseHhF7dXVqHTfJPCRDaEbid/drOhgitgYku/letMUqOXFoWV0zIIUbjpdH2t+tYj4bQMRQ==} + engines: {node: '>=8'} + strip-json-comments@3.1.1: resolution: {integrity: sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==} engines: {node: '>=8'} @@ -2734,6 +2915,10 @@ packages: webpack-cli: optional: true + whatwg-mimetype@3.0.0: + resolution: {integrity: sha512-nt+N2dzIutVRxARx1nghPKGv1xHikU7HKdfafKkLNLindmPU/ch3U31NOCGGA/dmPcmb1VlofO0vnKAcsm0o/Q==} + engines: {node: '>=12'} + which@1.3.1: resolution: {integrity: sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==} hasBin: true @@ -2771,6 +2956,18 @@ packages: resolution: {integrity: sha512-FdNA4RyH1L43TlvGG8qOMIfcEczwA5ij+zLXUy3Z83CjxhLvcV7/Q/8pk22wnCgYw7PJhtK+7lhO+qqyT4NdvQ==} engines: {node: '>=16.14'} + ws@8.21.2: + resolution: {integrity: sha512-54dMVAo4WIe6SKy3vBgN+9bJZqqQ8IMRevAkOLQALhi49qkkQDQfWdAZ8KQlXiEabw88ARXXdUrlvtbKQX+aKw==} + engines: {node: '>=10.0.0'} + peerDependencies: + bufferutil: ^4.0.1 + utf-8-validate: '>=5.0.2' + peerDependenciesMeta: + bufferutil: + optional: true + utf-8-validate: + optional: true + y18n@5.0.8: resolution: {integrity: sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==} engines: {node: '>=10'} @@ -2789,6 +2986,8 @@ packages: snapshots: + '@adobe/css-tools@4.5.0': {} + '@babel/code-frame@7.29.7': dependencies: '@babel/helper-validator-identifier': 7.29.7 @@ -2803,6 +3002,8 @@ snapshots: dependencies: '@babel/types': 7.29.7 + '@babel/runtime@7.29.7': {} + '@babel/types@7.29.7': dependencies: '@babel/helper-string-parser': 7.29.7 @@ -3608,6 +3809,37 @@ snapshots: estraverse: 5.3.0 picomatch: 4.0.5 + '@testing-library/dom@10.4.1': + dependencies: + '@babel/code-frame': 7.29.7 + '@babel/runtime': 7.29.7 + '@types/aria-query': 5.0.4 + aria-query: 5.3.0 + dom-accessibility-api: 0.5.16 + lz-string: 1.5.0 + picocolors: 1.1.1 + pretty-format: 27.5.1 + + '@testing-library/jest-dom@6.10.0(@testing-library/dom@10.4.1)': + dependencies: + '@adobe/css-tools': 4.5.0 + '@testing-library/dom': 10.4.1 + aria-query: 5.3.2 + css.escape: 1.5.1 + dom-accessibility-api: 0.6.3 + picocolors: 1.1.1 + redent: 3.0.0 + + '@testing-library/react@16.3.2(@testing-library/dom@10.4.1)(@types/react-dom@19.2.4(@types/react@19.2.18))(@types/react@19.2.18)(react-dom@19.2.8(react@19.2.8))(react@19.2.8)': + dependencies: + '@babel/runtime': 7.29.7 + '@testing-library/dom': 10.4.1 + react: 19.2.8 + react-dom: 19.2.8(react@19.2.8) + optionalDependencies: + '@types/react': 19.2.18 + '@types/react-dom': 19.2.4(@types/react@19.2.18) + '@trigen/oxlint-config@10.1.2(oxlint-tsgolint@0.25.0)(oxlint@1.74.0(oxlint-tsgolint@0.25.0))': dependencies: '@stylistic/eslint-plugin': 5.10.0 @@ -3631,6 +3863,8 @@ snapshots: tslib: 2.8.1 optional: true + '@types/aria-query@5.0.4': {} + '@types/chai@5.2.3': dependencies: '@types/deep-eql': 4.0.2 @@ -3650,6 +3884,20 @@ snapshots: '@types/picomatch@4.0.3': {} + '@types/react-dom@19.2.4(@types/react@19.2.18)': + dependencies: + '@types/react': 19.2.18 + + '@types/react@19.2.18': + dependencies: + csstype: 3.2.3 + + '@types/whatwg-mimetype@3.0.2': {} + + '@types/ws@8.18.1': + dependencies: + '@types/node': 22.20.1 + '@typescript-eslint/types@8.64.0': {} '@typescript/typescript-aix-ppc64@7.0.2': @@ -3712,6 +3960,11 @@ snapshots: '@typescript/typescript-win32-x64@7.0.2': optional: true + '@vitejs/plugin-react@6.0.5(vite@8.1.5(@types/node@22.20.1)(esbuild@0.28.1)(jiti@2.6.1)(terser@5.49.0))': + dependencies: + '@rolldown/pluginutils': 1.0.1 + vite: 8.1.5(@types/node@22.20.1)(esbuild@0.28.1)(jiti@2.6.1)(terser@5.49.0) + '@vitest/coverage-v8@4.1.10(vitest@4.1.10)': dependencies: '@bcoe/v8-coverage': 1.0.2 @@ -3724,7 +3977,7 @@ snapshots: obug: 2.1.4 std-env: 4.2.0 tinyrainbow: 3.1.0 - vitest: 4.1.10(@types/node@22.20.1)(@vitest/coverage-v8@4.1.10)(vite@8.1.5(@types/node@22.20.1)(esbuild@0.28.1)(jiti@2.6.1)(terser@5.49.0)) + vitest: 4.1.10(@types/node@22.20.1)(@vitest/coverage-v8@4.1.10)(happy-dom@20.11.1)(vite@8.1.5(@types/node@22.20.1)(esbuild@0.28.1)(jiti@2.6.1)(terser@5.49.0)) '@vitest/expect@4.1.10': dependencies: @@ -3889,12 +4142,20 @@ snapshots: dependencies: color-convert: 2.0.1 + ansi-styles@5.2.0: {} + ansi-styles@6.2.3: {} argparse@2.0.1: {} argue-cli@3.1.0: {} + aria-query@5.3.0: + dependencies: + dequal: 2.0.3 + + aria-query@5.3.2: {} + assertion-error@2.0.1: {} ast-v8-to-istanbul@1.0.5: @@ -3936,6 +4197,10 @@ snapshots: buffer-from@1.1.2: {} + buffer-image-size@0.6.4: + dependencies: + '@types/node': 22.20.1 + buffer@5.7.1: dependencies: base64-js: 1.5.1 @@ -4066,6 +4331,10 @@ snapshots: css-mediaquery@0.1.2: {} + css.escape@1.5.1: {} + + csstype@3.2.3: {} + cz-conventional-changelog@3.3.0(@types/node@22.20.1)(typescript@7.0.2): dependencies: chalk: 2.4.2 @@ -4102,12 +4371,18 @@ snapshots: presentable-error: 0.0.1 slash: 5.1.0 + dequal@2.0.3: {} + detect-file@1.0.0: {} detect-indent@6.1.0: {} detect-libc@2.1.2: {} + dom-accessibility-api@0.5.16: {} + + dom-accessibility-api@0.6.3: {} + electron-to-chromium@1.5.393: {} emoji-regex@10.6.0: {} @@ -4119,6 +4394,8 @@ snapshots: graceful-fs: 4.2.11 tapable: 2.3.3 + entities@7.0.1: {} + env-paths@2.2.1: {} error-ex@1.3.4: @@ -4301,6 +4578,19 @@ snapshots: graceful-fs@4.2.11: {} + happy-dom@20.11.1: + dependencies: + '@types/node': 22.20.1 + '@types/whatwg-mimetype': 3.0.2 + '@types/ws': 8.18.1 + buffer-image-size: 0.6.4 + entities: 7.0.1 + whatwg-mimetype: 3.0.0 + ws: 8.21.2 + transitivePeerDependencies: + - bufferutil + - utf-8-validate + has-flag@3.0.0: {} has-flag@4.0.0: {} @@ -4328,6 +4618,8 @@ snapshots: imurmurhash@0.1.4: {} + indent-string@4.0.0: {} + inflight@1.0.6: dependencies: once: 1.4.0 @@ -4498,6 +4790,8 @@ snapshots: longest@2.0.1: {} + lz-string@1.5.0: {} + magic-string@0.30.21: dependencies: '@jridgewell/sourcemap-codec': 1.5.5 @@ -4546,6 +4840,8 @@ snapshots: mimic-fn@2.1.0: {} + min-indent@1.0.1: {} + minimatch@3.1.5: dependencies: brace-expansion: 1.1.16 @@ -4673,8 +4969,23 @@ snapshots: presentable-error@0.0.1: {} + pretty-format@27.5.1: + dependencies: + ansi-regex: 5.0.1 + ansi-styles: 5.2.0 + react-is: 17.0.2 + queue-microtask@1.2.3: {} + react-dom@19.2.8(react@19.2.8): + dependencies: + react: 19.2.8 + scheduler: 0.27.0 + + react-is@17.0.2: {} + + react@19.2.8: {} + read-yaml-file@2.1.0: dependencies: js-yaml: 4.3.0 @@ -4691,6 +5002,11 @@ snapshots: string_decoder: 1.3.0 util-deprecate: 1.0.2 + redent@3.0.0: + dependencies: + indent-string: 4.0.0 + strip-indent: 3.0.0 + require-from-string@2.0.2: {} resolve-dir@1.0.1: @@ -4744,6 +5060,8 @@ snapshots: safer-buffer@2.1.2: {} + scheduler@0.27.0: {} + schema-utils@4.3.3: dependencies: '@types/json-schema': 7.0.15 @@ -4859,6 +5177,10 @@ snapshots: strip-comments-strings@1.2.0: {} + strip-indent@3.0.0: + dependencies: + min-indent: 1.0.1 + strip-json-comments@3.1.1: {} supports-color@5.5.0: @@ -4962,7 +5284,7 @@ snapshots: jiti: 2.6.1 terser: 5.49.0 - vitest@4.1.10(@types/node@22.20.1)(@vitest/coverage-v8@4.1.10)(vite@8.1.5(@types/node@22.20.1)(esbuild@0.28.1)(jiti@2.6.1)(terser@5.49.0)): + vitest@4.1.10(@types/node@22.20.1)(@vitest/coverage-v8@4.1.10)(happy-dom@20.11.1)(vite@8.1.5(@types/node@22.20.1)(esbuild@0.28.1)(jiti@2.6.1)(terser@5.49.0)): dependencies: '@vitest/expect': 4.1.10 '@vitest/mocker': 4.1.10(vite@8.1.5(@types/node@22.20.1)(esbuild@0.28.1)(jiti@2.6.1)(terser@5.49.0)) @@ -4987,6 +5309,7 @@ snapshots: optionalDependencies: '@types/node': 22.20.1 '@vitest/coverage-v8': 4.1.10(vitest@4.1.10) + happy-dom: 20.11.1 transitivePeerDependencies: - msw @@ -5038,6 +5361,8 @@ snapshots: - postcss - uglify-js + whatwg-mimetype@3.0.0: {} + which@1.3.1: dependencies: isexe: 2.0.0 @@ -5077,6 +5402,8 @@ snapshots: js-yaml: 4.3.0 write-file-atomic: 5.0.1 + ws@8.21.2: {} + y18n@5.0.8: {} yargs-parser@22.0.0: {}