diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index d63c35c7..8957896e 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -9,17 +9,18 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 + - uses: pnpm/action-setup@v6 - uses: actions/setup-node@v3 with: - cache: 'yarn' + cache: 'pnpm' - name: Install Dependencies - run: yarn install --frozen-lockfile + run: pnpm install --frozen-lockfile - name: Check build health - run: yarn build + run: pnpm build - name: Check for regressions - run: yarn eslint:ci + run: pnpm eslint:ci - name: Run tests - run: yarn test + run: pnpm test diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index afe9bc8c..1f694988 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -42,12 +42,14 @@ jobs: - name: Check out repository uses: actions/checkout@v6 + - uses: pnpm/action-setup@v6 + - name: Set up Node.js uses: actions/setup-node@v6 with: node-version: 24 registry-url: https://registry.npmjs.org - package-manager-cache: false + cache: 'pnpm' - name: Verify release version env: @@ -60,16 +62,16 @@ jobs: fi - name: Install dependencies - run: yarn install --frozen-lockfile + run: pnpm install --frozen-lockfile - name: Lint - run: yarn eslint:ci + run: pnpm eslint:ci - name: Test - run: yarn test + run: pnpm test - name: Build - run: yarn build + run: pnpm build - name: Publish package env: diff --git a/.gitignore b/.gitignore index f322062f..d10826f1 100644 --- a/.gitignore +++ b/.gitignore @@ -16,7 +16,6 @@ yarn-error.log .size-snapshot.json pnpm-debug.log .parcel-cache -pnpm-lock.yaml storybook-static *.tgz \ No newline at end of file diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index d5658be2..66d3ec86 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -5,7 +5,7 @@ Thanks for wanting to make a contribution and wanting to improve this library fo ## How to Contribute 1. Fork and clone the repo -2. Run `yarn install` to install dependencies +2. Run `pnpm install` to install dependencies 3. Create a branch for your PR with `git checkout -b pr-type/issue-number-your-branch-name` 4. Let's get cooking! 👨🏻‍🍳🥓 diff --git a/docs/custom-effects.mdx b/docs/custom-effects.mdx new file mode 100644 index 00000000..c3e6f5b1 --- /dev/null +++ b/docs/custom-effects.mdx @@ -0,0 +1,85 @@ +--- +title: Custom effects +description: Wrapping your own effects, or postprocessing's, as components +nav: 0 +--- + +Most effects from `postprocessing` are already wrapped by this library, but if you need one that isn't, or want to write your own, there are three ways to do it depending on what the effect's constructor needs. + +## Zero-arg effects (recommended) + +If the effect's constructor works with zero arguments (`new SomeEffect()`), `createEffectComponent` gives you a component with live-updating props for free - no `useMemo`/`useDispose` needed, and no reconstruction on every prop change. This is exactly how this library's own simple effects (`BrightnessContrast`, `ChromaticAberration`, `Noise`, ...) are built: + +```jsx +import { SomeEffect } from 'postprocessing' +import { createEffectComponent } from '@react-three/postprocessing' + +export const SomeEffectComponent = createEffectComponent(SomeEffect) +``` + +Every constructor option becomes a live prop that updates the existing instance in place, and `blendFunction`/`opacity` are supported automatically. `ref` resolves to the effect instance. Disposal is handled for you - unlike ``, r3f disposes elements it constructed itself. + +### Custom defaults + +If you want different defaults than the class's own, or the constructor takes a positional argument instead of an options object, wrap it in a thin component - this is how this library's own `Pixelation` is built (its default `granularity` is `5`, not the class's own `30`): + +```jsx +import { PixelationEffect } from 'postprocessing' +import { createEffectComponent } from '@react-three/postprocessing' + +const PixelationImpl = createEffectComponent(PixelationEffect) + +export function Pixelation({ granularity = 5, ...props }) { + return +} +``` + +## Effects that need real constructor arguments + +Effects whose constructor needs more than zero arguments - e.g. `OutlineEffect(scene, camera, options)` - can't use `createEffectComponent`: it relies on r3f's `extend()`, which always constructs via `new Effect()`. Build these by hand instead: `useMemo`/`useDispose` for construction, `useLiveDefaults` for props that should update the existing instance rather than reconstruct it. + +```jsx +import { useMemo } from 'react' +import { SomeEffect } from 'postprocessing' +import { useDispose, useLiveDefaults } from '@react-three/postprocessing' + +const LIVE_KEYS = ['someProp', 'anotherProp'] + +export function SomeEffectComponent({ requiredArg, someProp, anotherProp, ref }) { + const effect = useMemo(() => new SomeEffect(requiredArg), [requiredArg]) + + useLiveDefaults(effect, { someProp, anotherProp }, LIVE_KEYS) + useDispose(effect) + + return +} +``` + +`LIVE_KEYS` should only list options that have a real setter on the class - check the effect's own source. `useLiveDefaults` resets a prop to the effect's constructor-time default when it's removed, and only calls the setter when the resolved value actually changed (some setters have side effects beyond storing the value). See `Outline.tsx` in this repo for a real example, including piercing into a nested property like `blendMode.blendFunction`. + +## Writing a brand new effect + +For effects that don't exist in `postprocessing` at all, extend `Effect` and wrap the result the same way as any other zero-arg effect: + +```jsx +import { Effect } from 'postprocessing' +import { Uniform } from 'three' +import { createEffectComponent } from '@react-three/postprocessing' + +const fragmentShader = `some_shader_code` + +class MyCustomEffect extends Effect { + constructor({ param = 0.1 } = {}) { + super('MyCustomEffect', fragmentShader, { + uniforms: new Map([['param', new Uniform(param)]]), + }) + } + + update(renderer, inputBuffer, deltaTime) { + // read/write per-frame state on `this` (e.g. this.uniforms.get('param').value = ...), + // never on a module-level variable - that would be shared across every instance + } +} + +export const MyCustomEffectComponent = createEffectComponent(MyCustomEffect) +``` diff --git a/docs/effect-composer.mdx b/docs/effect-composer.mdx index ffcc9757..ae2370af 100644 --- a/docs/effect-composer.mdx +++ b/docs/effect-composer.mdx @@ -12,6 +12,8 @@ The `EffectComposer` must wrap all your effects. It will manage them for you. enableNormalPass?: boolean stencilBuffer?: boolean autoClear?: boolean + /** Whether the last pass automatically renders to the screen. Set to `false` when rendering to a target of your own (e.g. via a trailing CopyPass) instead. */ + autoRenderToScreen?: boolean multisampling?: number frameBufferType?: TextureDataType /** For effects that support DepthDownsamplingPass */ @@ -19,6 +21,10 @@ The `EffectComposer` must wrap all your effects. It will manage them for you. renderPriority?: number camera?: THREE.Camera scene?: THREE.Scene + /** Constructs the initial pass that renders the scene - override to substitute your own RenderPass-like implementation. Defaults to `(scene, camera) => new RenderPass(scene, camera)`. */ + renderPass?: (scene: THREE.Scene, camera: THREE.Camera) => Pass + /** Controls how effects are merged into EffectPass instances. `'auto'` (default) merges consecutive effects into one pass, keeping at most one convolution effect (e.g. DepthOfField) per pass - matching what postprocessing actually supports. `'all'` merges without that limit - only enable it if you've verified your specific combination works, since multiple convolution effects sharing a pass throws at render time. `'none'` disables merging entirely, giving every effect its own pass. */ + mergeMode?: 'auto' | 'all' | 'none' > {/* your effects go here */} diff --git a/docs/effect-group.mdx b/docs/effect-group.mdx new file mode 100644 index 00000000..3c927d70 --- /dev/null +++ b/docs/effect-group.mdx @@ -0,0 +1,48 @@ +--- +title: EffectGroup +description: Explicit effect grouping with a pass-level enable toggle +nav: 0 +--- + +`EffectComposer` groups effects into passes automatically: consecutive effects +merge into one `EffectPass`, based only on what happens to sit next to them in +JSX. `EffectGroup` lets you group specific effects into one pass explicitly, +regardless of what's around them, and gives that whole pass a cheap +`enabled` toggle - the same thing `Pass.enabled` gives you in vanilla +`postprocessing`, without reconstructing anything. + +```jsx + + {/* effects to group go here */} + +``` + +```jsx + + + + + + + +``` + +`Bloom` still gets its own pass as usual. `Vignette` and `Noise` are merged +into one pass together, positioned exactly where `EffectGroup` sits in the +JSX, and toggling `enabled` turns that whole pass on or off without rebuilding +it - so it's cheap to flip every frame if you need to. + +`ref` resolves to the underlying `EffectPass`: + +```jsx +... +``` + +## Limitations + +- `EffectGroup` only groups `postprocessing` `Effect`s - things that merge + into one shader pass. A handful of effects in this library (like `N8AO`) + are full standalone passes, not effects, and can't be grouped this way - + they take their own `enabled` prop instead. diff --git a/docs/effects/ascii.mdx b/docs/effects/ascii.mdx index 8755efee..5f8d4bcd 100644 --- a/docs/effects/ascii.mdx +++ b/docs/effects/ascii.mdx @@ -28,5 +28,5 @@ return ( | characters | String | ` .:,'-^=*+?!\|0#X%WM@` | The characters to sample from, ordered from "empty" to "dense". | | fontSize | Number | 54 | The font size used to draw the character atlas. | | cellSize | Number | 16 | The size of each character cell, in pixels. | -| color | String | '#ffffff' | The color of the characters. | +| color | ColorRepresentation | '#ffffff' | The color of the characters. | | invert | Boolean | false | Inverts which characters map to bright vs dark pixels. | diff --git a/docs/effects/autofocus.mdx b/docs/effects/autofocus.mdx index bad1e2c8..9d290cab 100644 --- a/docs/effects/autofocus.mdx +++ b/docs/effects/autofocus.mdx @@ -5,7 +5,7 @@ nav: 1 An auto-focus effect, that extends ``. -Based on [ektogamat/AutoFocusDOF](https://github.com/ektogamat/AutoFocusDOF). +Based on [ektogamat/AutoFocusDOF](https://github.com/ektogamat/AutoFocusDOF). Built on `` and `useDepthPicking` internally - use those directly if you want a picked position for something other than ``'s own focus target. ```tsx export type AutofocusProps = typeof DepthOfField & { diff --git a/docs/effects/custom-effects.mdx b/docs/effects/custom-effects.mdx deleted file mode 100644 index cbcf635d..00000000 --- a/docs/effects/custom-effects.mdx +++ /dev/null @@ -1,54 +0,0 @@ ---- -title: Custom effects -nav: 2 ---- - -If you plan to use custom effects, make sure to expose the effect itself as a primitive! - -r3f never disposes objects rendered via `` (their state may be owned outside -React), so effects rendered this way must dispose themselves - use the `useDispose` hook exported by -this library for that. - -```jsx -import { useMemo } from 'react' -import { PixelationEffect } from 'postprocessing' -import { useDispose } from '@react-three/postprocessing' - -export function Pixelation({ granularity = 5, ref }) { - const effect = useMemo(() => new PixelationEffect(granularity), [granularity]) - useDispose(effect) - return -} -``` - -For effects that aren't present in `postprocessing` you should extend the `Effect` class: - -```jsx -import { useMemo } from 'react' -import { Uniform } from 'three' -import { Effect } from 'postprocessing' -import { useDispose } from '@react-three/postprocessing' - -const fragmentShader = `some_shader_code` - -// Effect implementation -class MyCustomEffectImpl extends Effect { - constructor({ param = 0.1 } = {}) { - super('MyCustomEffect', fragmentShader, { - uniforms: new Map([['param', new Uniform(param)]]), - }) - } - - update(renderer, inputBuffer, deltaTime) { - // read/write per-frame state on `this` (e.g. this.uniforms.get('param').value = ...), - // never on a module-level variable - that would be shared across every instance - } -} - -// Effect component -export function MyCustomEffect({ param, ref }) { - const effect = useMemo(() => new MyCustomEffectImpl({ param }), [param]) - useDispose(effect) - return -} -``` diff --git a/docs/effects/god-rays.mdx b/docs/effects/god-rays.mdx index 9e859e45..aa19f570 100644 --- a/docs/effects/god-rays.mdx +++ b/docs/effects/god-rays.mdx @@ -5,6 +5,8 @@ nav: 1 The GodRays effect requires a mesh that will be used as an origin point for the rays. Refer to this [example](https://pmndrs.github.io/examples/take-control) for more details. +For correct occlusion by other objects, also set ``. + ```jsx import { GodRays } from '@react-three/postprocessing' diff --git a/docs/effects/outline.mdx b/docs/effects/outline.mdx index b4cc6288..98ee73a1 100644 --- a/docs/effects/outline.mdx +++ b/docs/effects/outline.mdx @@ -5,6 +5,8 @@ nav: 1 An outline effect. +Requires `` - without it, outlines don't render at all. + ```jsx import { Outline } from '@react-three/postprocessing' import { BlendFunction, Resolution, KernelSize } from 'postprocessing' @@ -37,21 +39,23 @@ return ( ## Props -| Name | Type | Default | Description | -| ---------------- | ------------- | --------------------- | -------------------------------------------------------------------------- | -| selection | Objects | | Selection of objects that will be outlined | -| selectionLayer | Number | 10 | The selection layer | -| blendFunction | BlendFunction | BlendFunction.SCREEN | The blend function of this effect. | -| patternTexture | Texture | null | A pattern texture. | -| patternScale | Number | 1.0 | The pattern texture scale. | -| edgeStrength | Number | 1.0 | The edge strength. | -| pulseSpeed | Number | 0 | The pulse speed. A value of zero disables the pulse effect. | -| visibleEdgeColor | Number | 0xffffff | The color of visible edges. | -| hiddenEdgeColor | Number | 0x22090a | The color of hidden edges. | -| multisampling | Number | 0 | The number of samples used for multisample antialiasing. Requires WebGL 2. | -| resolutionScale | Number | 0.5 | The render resolution scale. | -| resolutionX | Number | Resolution.AUTO_SIZE | The render width. | -| resolutionY | Number | Resolution.AUTO_SIZE | The render height. | -| kernelSize | KernelSize | KernelSize.VERY_SMALL | The blur kernel size. | -| blur | Boolean | false | Whether the outline should be blurred. | -| xRay | Boolean | true | Whether occluded parts of selected objects should be visible. | +| Name | Type | Default | Description | +| ---------------- | -------------------- | ----------------------- | ------------------------------------------------------------------------------------- | +| selection | Objects | | Selection of objects that will be outlined | +| selectionLayer | Number | 10 | The selection layer | +| blendFunction | BlendFunction | BlendFunction.SCREEN | The blend function of this effect. | +| patternTexture | THREE.Texture | null | A pattern texture. | +| patternScale | Number | 1.0 | The pattern texture scale. | +| edgeStrength | Number | 1.0 | The edge strength. | +| pulseSpeed | Number | 0 | The pulse speed. A value of zero disables the pulse effect. | +| visibleEdgeColor | ColorRepresentation | 0xffffff | The color of visible edges. | +| hiddenEdgeColor | ColorRepresentation | 0x22090a | The color of hidden edges. | +| multisampling | Number | 0 | The number of samples used for multisample antialiasing. Requires WebGL 2. | +| resolutionScale | Number | 0.5 | The render resolution scale. Applied at construction only. | +| resolutionX | Number | Resolution.AUTO_SIZE | The render width. Applied at construction only. | +| resolutionY | Number | Resolution.AUTO_SIZE | The render height. Applied at construction only. | +| width | Number | Resizer.AUTO_SIZE | The render width. Unlike `resolutionX`, updates live without reconstructing the effect. | +| height | Number | Resizer.AUTO_SIZE | The render height. Unlike `resolutionY`, updates live without reconstructing the effect. | +| kernelSize | KernelSize | KernelSize.VERY_SMALL | The blur kernel size. | +| blur | Boolean | false | Whether the outline should be blurred. | +| xRay | Boolean | true | Whether occluded parts of selected objects should be visible. | diff --git a/docs/effects/shockwave.mdx b/docs/effects/shockwave.mdx index 68027bb1..e02b2d6e 100644 --- a/docs/effects/shockwave.mdx +++ b/docs/effects/shockwave.mdx @@ -29,9 +29,9 @@ return ( ## Props -| Name | Type | Default | Description | -| --------- | ------- | -------- | ------------------------------------- | -| position | Vector3 | (0,0,0) | The world position of the shockwave. | +| Name | Type | Default | Description | +| --------- | ----------------------------- | -------- | ------------------------------------- | +| position | Vector3 \| [x, y, z] \| Number | (0,0,0) | The world position of the shockwave. | | speed | Number | 2.0 | The animation speed. | | maxRadius | Number | 1.0 | The extent of the shockwave. | | waveSize | Number | 0.2 | The wave size. | diff --git a/docs/passes/depth-picking.mdx b/docs/passes/depth-picking.mdx new file mode 100644 index 00000000..dac57f01 --- /dev/null +++ b/docs/passes/depth-picking.mdx @@ -0,0 +1,73 @@ +--- +title: DepthPicking +nav: 1 +--- + +Mounts a `postprocessing` `DepthPickingPass` and exposes its `readDepth` via ref - nothing else. Renders nothing, updates nothing automatically. Pair it with `useDepthPicking` for a world-space position instead of raw depth. + +`` is built on both of these - use them directly when you want a picked position for something other than ``'s own focus target. + +```tsx + + + +``` + +Ref-api: + +```tsx +type DepthPickingApi = { + readDepth: (ndc: THREE.Vector2 | THREE.Vector3) => Promise +} +``` + +## `useDepthPicking` + +```tsx +function useDepthPicking( + pass: RefObject, + camera?: THREE.Camera, // defaults to the composer's camera, then r3f's own +): (x: number, y: number) => Promise +``` + +A hook that turns a screen position into a world-space point, using a mounted ``'s `readDepth`. Returns `false` if nothing was hit. Call it whenever you want - on click, on hover, every frame - nothing runs on its own. It only needs a ref to the mounted pass, not the `` context itself, so it works anywhere under ``: + +```tsx +const pickRef = useRef(null) + +function Cursor() { + const getHit = useDepthPicking(pickRef) + const meshRef = useRef(null) + useFrame(async ({ pointer }) => { + const hit = await getHit(pointer.x, pointer.y) + if (hit) meshRef.current?.position.copy(hit) + }) + return ( + + + {/* depthWrite false - see the warning below */} + + + ) +} + +return ( + <> + + + + + +) +``` + +It unprojects using the ``'s own camera when called from inside it - the same one the pass renders depth from - falling back to r3f's own camera otherwise. Called from outside that `` (like `Cursor` above) with a non-default camera, pass that same camera as the second argument explicitly. + +**If you render something at the picked position** (a cursor, a placement preview, ...), give its material `depthWrite={false}`. Depth is read from the same buffer everything else renders into - without this, your own marker sits at the last hitpoint, gets sampled by the *next* pick as the closest surface there, and the marker creeps toward the camera every frame, faster as it gets closer, until it resets and repeats. (`Autofocus`'s own debug markers already do this.) + +Click-to-pick instead of every frame: + +```tsx +const hit = await getHit(pointerNdcX, pointerNdcY) +if (hit) character.moveTo(hit) +``` diff --git a/docs/effects/n8ao.mdx b/docs/passes/n8ao.mdx similarity index 84% rename from docs/effects/n8ao.mdx rename to docs/passes/n8ao.mdx index b05dde6e..7008b4bf 100644 --- a/docs/effects/n8ao.mdx +++ b/docs/passes/n8ao.mdx @@ -3,16 +3,17 @@ title: N8AO nav: 1 --- -A fast, high quality ambient occlusion effect, wrapping [N8python/n8ao](https://github.com/N8python/n8ao). Self-contained - unlike [SSAO](/effects/ssao), it does not need `enableNormalPass` on ``. +A fast, high quality ambient occlusion pass, wrapping [N8python/n8ao](https://github.com/N8python/n8ao). Self-contained - unlike [SSAO](/effects/ssao), it does not need `enableNormalPass` on ``. ```jsx import { N8AO } from '@react-three/postprocessing' return ( = 0.182.0" } -} +} \ No newline at end of file diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml new file mode 100644 index 00000000..bbdca882 --- /dev/null +++ b/pnpm-lock.yaml @@ -0,0 +1,3712 @@ +lockfileVersion: '9.0' + +settings: + autoInstallPeers: true + excludeLinksFromLockfile: false + +importers: + + .: + dependencies: + maath: + specifier: ^0.10.8 + version: 0.10.8(@types/three@0.182.0)(three@0.182.0) + n8ao: + specifier: ^2.0.0 + version: 2.0.1(postprocessing@6.39.4(three@0.182.0))(three@0.182.0) + devDependencies: + '@eslint/compat': + specifier: ^2.1.0 + version: 2.1.0(eslint@9.39.5(supports-color@7.2.0)) + '@eslint/eslintrc': + specifier: ^3.3.6 + version: 3.3.6(supports-color@7.2.0) + '@eslint/js': + specifier: ^9.39.5 + version: 9.39.5 + '@react-three/fiber': + specifier: ^9.7.0 + version: 9.7.0(@types/react@19.2.18)(react@19.2.8)(three@0.182.0) + '@types/node': + specifier: ^26.1.1 + version: 26.2.0 + '@types/react': + specifier: ^19.2.17 + version: 19.2.18 + '@types/three': + specifier: ^0.182.0 + version: 0.182.0 + '@typescript-eslint/eslint-plugin': + specifier: ^8.64.0 + version: 8.67.0(@typescript-eslint/parser@8.67.0(eslint@9.39.5(supports-color@7.2.0))(supports-color@7.2.0)(typescript@6.0.3))(eslint@9.39.5(supports-color@7.2.0))(supports-color@7.2.0)(typescript@6.0.3) + '@typescript-eslint/parser': + specifier: ^8.64.0 + version: 8.67.0(eslint@9.39.5(supports-color@7.2.0))(supports-color@7.2.0)(typescript@6.0.3) + eslint: + specifier: ^9.0.0 + version: 9.39.5(supports-color@7.2.0) + eslint-config-prettier: + specifier: ^10.1.8 + version: 10.1.8(eslint@9.39.5(supports-color@7.2.0)) + eslint-import-resolver-alias: + specifier: ^1.1.2 + version: 1.1.2(eslint-plugin-import@2.32.0(@typescript-eslint/parser@8.67.0(eslint@9.39.5(supports-color@7.2.0))(supports-color@7.2.0)(typescript@6.0.3))(eslint@9.39.5(supports-color@7.2.0))(supports-color@7.2.0)) + eslint-plugin-import: + specifier: ^2.32.0 + version: 2.32.0(@typescript-eslint/parser@8.67.0(eslint@9.39.5(supports-color@7.2.0))(supports-color@7.2.0)(typescript@6.0.3))(eslint@9.39.5(supports-color@7.2.0))(supports-color@7.2.0) + eslint-plugin-prettier: + specifier: ^5.5.6 + version: 5.5.6(eslint-config-prettier@10.1.8(eslint@9.39.5(supports-color@7.2.0)))(eslint@9.39.5(supports-color@7.2.0))(prettier@3.9.6) + eslint-plugin-react: + specifier: ^7.37.5 + version: 7.37.5(eslint@9.39.5(supports-color@7.2.0)) + eslint-plugin-react-hooks: + specifier: ^7.1.1 + version: 7.1.1(eslint@9.39.5(supports-color@7.2.0))(supports-color@7.2.0) + globals: + specifier: ^17.7.0 + version: 17.11.0 + postprocessing: + specifier: ^6.39.3 + version: 6.39.4(three@0.182.0) + prettier: + specifier: ^3.9.5 + version: 3.9.6 + react: + specifier: ^19.2.7 + version: 19.2.8 + three: + specifier: ^0.182.0 + version: 0.182.0 + typescript: + specifier: ^6.0.0 + version: 6.0.3 + vite: + specifier: ^8.1.4 + version: 8.2.1(@types/node@26.2.0) + vitest: + specifier: ^4.1.10 + version: 4.1.10(@types/node@26.2.0)(vite@8.2.1(@types/node@26.2.0)) + +packages: + + '@babel/code-frame@7.29.7': + resolution: {integrity: sha512-Aup7aUOfpbAUg2ROOJN6Iw5f9DMBlzu0mIkm/malLQFN/YQgO48wCj0Kxa3sEHJvPVFg7siR+qRInwXd2qhQKw==} + engines: {node: '>=6.9.0'} + + '@babel/compat-data@7.29.7': + resolution: {integrity: sha512-locTkQyKvwIEgBzVrn8693ebc97F2U8ZHjbXwDXJ5Fn2TCpNwTlKcaKLkdHop5c/icOFE7qt7Q9JC5hnKNa6Gg==} + engines: {node: '>=6.9.0'} + + '@babel/core@7.29.7': + resolution: {integrity: sha512-RgHBCvtjbOK2gXSNBNIkNoEc9qoVEtau3hj8gEqKQuL3HZAibKarWFEI3Lfm6EYKkLalOh8eSrj9b+ch9H/VBA==} + engines: {node: '>=6.9.0'} + + '@babel/generator@7.29.8': + resolution: {integrity: sha512-gZbepsdh3WDtgZKWL+vTPh71LSBrm/Y4/QDZBVCcYfmeTEEuoOYwlSy+G1StfJg+/Zy550u/3TATbm7qDbbMtg==} + engines: {node: '>=6.9.0'} + + '@babel/helper-compilation-targets@7.29.7': + resolution: {integrity: sha512-wem6WaBj4NaVYVdNhLPPVacES6ZJ+KBBfSkTMD3YZxbP3rm3Di85tJU5ljaUNhaOynt+Aj0xruhYuzQBt8n71g==} + engines: {node: '>=6.9.0'} + + '@babel/helper-globals@7.29.7': + resolution: {integrity: sha512-3nQVUAtvkKH9zahfWgw96Jc/uFOmjACE1kQz82E2lqWmHBgjzbNlsC22nuQTfahmWeQtTq5nQ/4Nnd2A1wj4zA==} + engines: {node: '>=6.9.0'} + + '@babel/helper-module-imports@7.29.7': + resolution: {integrity: sha512-ejHwrQQYcm9xnTivShn2IDOlIzInN34AXskvq9QicvCtEzq1Vzclu/tKF8Jq1Cg8JG2GL6/EmjgsCT7lXepE3g==} + engines: {node: '>=6.9.0'} + + '@babel/helper-module-transforms@7.29.7': + resolution: {integrity: sha512-UPUVSyXbOh627KiCIGQSgwWzGeBKLkaJ9PJEdrngIwMSzxLR4jS4+f1f1jb7VzBbg8nFLaYotvVPFCTqdrmTAg==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0 + + '@babel/helper-string-parser@7.29.7': + resolution: {integrity: sha512-Pb5ijPrZ89GDH8223L4UP8i6QApWxs04RbPQJTeWDV0/keR2E36MeKnyr6LYmUUvqRRI+Iv87SuF1W6ErINzYw==} + engines: {node: '>=6.9.0'} + + '@babel/helper-validator-identifier@7.29.7': + resolution: {integrity: sha512-qehxGkRj55h/ff8EMaJ+cYhyaKlHIxqYDn682wQD7RNp9UujOQsHog2uS0r2vzr4pW+sXf90NeeayjcNaX3fFg==} + engines: {node: '>=6.9.0'} + + '@babel/helper-validator-option@7.29.7': + resolution: {integrity: sha512-N9ZErrD+yW5geCDtBqnOoxmR8+tNKiGuxKlDpuJxfsqpa2dFcexaziGAE/qoHLiDDreVNMupxGmSoNlyvsA3gw==} + engines: {node: '>=6.9.0'} + + '@babel/helpers@7.29.7': + resolution: {integrity: sha512-1k2lAGRMfHTcwuNYcCNUmaUffmQv8KWMfh2iJUUeRlwlwH4FdNG7mfPI10NPfLHJFThE4Tyr4mv7kTNZOiPuBg==} + engines: {node: '>=6.9.0'} + + '@babel/parser@7.29.8': + resolution: {integrity: sha512-E8lTAYNB1KW+FH+VGJuZM1ioAx2E6oVlvQFRrf5P8ZZmsiJXYAD9vTFV7yyEURNzgh1dFqMZuO6tUwcARbqFCA==} + 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/template@7.29.7': + resolution: {integrity: sha512-puq+Gf35oI24FeN11LkoUQFqv9uwNeWpxXZi/Ji3rRIoKAzKnxRaZ+Gkj0vKS9ZCiTESfng1N9LyOyXvo+m+Gg==} + engines: {node: '>=6.9.0'} + + '@babel/traverse@7.29.8': + resolution: {integrity: sha512-I5z7H3bf/41ktsNVLtpN0wAa336HkqIHQ5BuPLEhTkt1jVSyZpeNKIzTgEWmlxjdg81R0IgUCcaE+Ok3NvrfZg==} + engines: {node: '>=6.9.0'} + + '@babel/types@7.29.8': + resolution: {integrity: sha512-Vj1jF3cPfxg7OAfoI7QnVKLoILlm2JF9pnVHrX8qx7AHMiYWT+NDAA7jChlNgRS4WTLc/fD1lXLmPixluj+3Gg==} + engines: {node: '>=6.9.0'} + + '@dimforge/rapier3d-compat@0.12.0': + resolution: {integrity: sha512-uekIGetywIgopfD97oDL5PfeezkFpNhwlzlaEYNOA0N6ghdsOvh/HYjSMek5Q2O1PYvRSDFcqFVJl4r4ZBwOow==} + + '@eslint-community/eslint-utils@4.10.1': + resolution: {integrity: sha512-cuadcxVFE8sDK6iWJbs8Sn0av2Nrh2QSGQhVlBW9AaAHqHwjWsZHT8LJ4hFGPh7ASBV2deFdM7H/DPjulmh8rg==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + peerDependencies: + eslint: ^6.0.0 || ^7.0.0 || >=8.0.0 + + '@eslint-community/regexpp@4.12.2': + resolution: {integrity: sha512-EriSTlt5OC9/7SXkRSCAhfSxxoSUgBm33OH+IkwbdpgoqsSsUg7y3uh+IICI/Qg4BBWr3U2i39RpmycbxMq4ew==} + engines: {node: ^12.0.0 || ^14.0.0 || >=16.0.0} + + '@eslint/compat@2.1.0': + resolution: {integrity: sha512-LgaSCymEpw7tF53xvDw9SNsraPb1IBHxpdABIOM0hW8UAlP8znrjYtuxfR58FSJ3L9BhwD+FaPRFQpZq84Nh6g==} + engines: {node: ^20.19.0 || ^22.13.0 || >=24} + peerDependencies: + eslint: ^8.40 || 9 || 10 + peerDependenciesMeta: + eslint: + optional: true + + '@eslint/config-array@0.21.2': + resolution: {integrity: sha512-nJl2KGTlrf9GjLimgIru+V/mzgSK0ABCDQRvxw5BjURL7WfH5uoWmizbH7QB6MmnMBd8cIC9uceWnezL1VZWWw==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@eslint/config-helpers@0.4.2': + resolution: {integrity: sha512-gBrxN88gOIf3R7ja5K9slwNayVcZgK6SOUORm2uBzTeIEfeVaIhOpCtTox3P6R7o2jLFwLFTLnC7kU/RGcYEgw==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@eslint/core@0.17.0': + resolution: {integrity: sha512-yL/sLrpmtDaFEiUj1osRP4TI2MDz1AddJL+jZ7KSqvBuliN4xqYY54IfdN8qD8Toa6g1iloph1fxQNkjOxrrpQ==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@eslint/core@1.2.1': + resolution: {integrity: sha512-MwcE1P+AZ4C6DWlpin/OmOA54mmIZ/+xZuJiQd4SyB29oAJjN30UW9wkKNptW2ctp4cEsvhlLY/CsQ1uoHDloQ==} + engines: {node: ^20.19.0 || ^22.13.0 || >=24} + + '@eslint/eslintrc@3.3.6': + resolution: {integrity: sha512-l2Ul9PrHsPCKcEY/ac7VgFj9D80C7S68sOKc618SyHDPK36s1XcFebXY0iTzUVn4Yq+YbwvSnDmCz9yxjX+QrA==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@eslint/js@9.39.5': + resolution: {integrity: sha512-QywQuszQh77pIXCsq998c8hbhSTI/azTty1Z6N53dmAudKHhy573j3yvRLsX2BSp8YpLtoCEG8E9DJe+8zUh4A==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@eslint/object-schema@2.1.7': + resolution: {integrity: sha512-VtAOaymWVfZcmZbp6E2mympDIHvyjXs/12LqWYjVw6qjrfF+VK+fyG33kChz3nnK+SU5/NeHOqrTEHS8sXO3OA==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@eslint/plugin-kit@0.4.1': + resolution: {integrity: sha512-43/qtrDUokr7LJqoF2c3+RInu/t4zfrpYdoSDfYyhg52rwLV6TnOvdG4fXm7IkSB3wErkcmJS9iEhjVtOSEjjA==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@humanfs/core@0.19.2': + resolution: {integrity: sha512-UhXNm+CFMWcbChXywFwkmhqjs3PRCmcSa/hfBgLIb7oQ5HNb1wS0icWsGtSAUNgefHeI+eBrA8I1fxmbHsGdvA==} + engines: {node: '>=18.18.0'} + + '@humanfs/node@0.16.8': + resolution: {integrity: sha512-gE1eQNZ3R++kTzFUpdGlpmy8kDZD/MLyHqDwqjkVQI0JMdI1D51sy1H958PNXYkM2rAac7e5/CnIKZrHtPh3BQ==} + engines: {node: '>=18.18.0'} + + '@humanfs/types@0.15.0': + resolution: {integrity: sha512-ZZ1w0aoQkwuUuC7Yf+7sdeaNfqQiiLcSRbfI08oAxqLtpXQr9AIVX7Ay7HLDuiLYAaFPu8oBYNq/QIi9URHJ3Q==} + engines: {node: '>=18.18.0'} + + '@humanwhocodes/module-importer@1.0.1': + resolution: {integrity: sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==} + engines: {node: '>=12.22'} + + '@humanwhocodes/retry@0.4.3': + resolution: {integrity: sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ==} + engines: {node: '>=18.18'} + + '@jridgewell/gen-mapping@0.3.13': + resolution: {integrity: sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==} + + '@jridgewell/remapping@2.3.5': + resolution: {integrity: sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==} + + '@jridgewell/resolve-uri@3.1.2': + resolution: {integrity: sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==} + engines: {node: '>=6.0.0'} + + '@jridgewell/sourcemap-codec@1.5.5': + resolution: {integrity: sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==} + + '@jridgewell/trace-mapping@0.3.31': + resolution: {integrity: sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==} + + '@oxc-project/types@0.144.0': + resolution: {integrity: sha512-nuhZIOLuI6TFQ32I/WnUx+SCPY7SdSKwgnFHydAuoS1+Z4BRcaP+RRJmGzl9lw+0OFF7UmaESf7KQRXaNLHypg==} + + '@pkgr/core@0.3.6': + resolution: {integrity: sha512-SEeaJLb3qBNF/OaXnaR1NmmBbFYk1zC0ZH/52fATcRPLFg/p791YrcyFFy44Bo9sLaGuSuLp5Q6axbb/O+v/RA==} + engines: {node: ^14.18.0 || >=16.0.0} + + '@react-three/fiber@9.7.0': + resolution: {integrity: sha512-EWm9FwcaOZQu/ExFW5rggoCMM1NJet5YbxVxKaOE+KSncrjU0Wx7017qSyGFvupviK89nMYGCWU3BIK4dI1clw==} + peerDependencies: + expo: '>=43.0' + expo-asset: '>=8.4' + expo-file-system: '>=11.0' + expo-gl: '>=11.0' + react: '>=19 <19.3' + react-dom: '>=19 <19.3' + react-native: '>=0.78' + three: '>=0.156' + peerDependenciesMeta: + expo: + optional: true + expo-asset: + optional: true + expo-file-system: + optional: true + expo-gl: + optional: true + react-dom: + optional: true + react-native: + optional: true + + '@rolldown/binding-android-arm64@1.2.4': + resolution: {integrity: sha512-jHC2cnyKz5xU2fhECtFl8OZ83cYNt13GZQD+0uMJ/X3o+ijmd56okHhTUwxVSHPx1IRVIJEZ1/1pPzeLCU6XKA==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [android] + + '@rolldown/binding-darwin-arm64@1.2.4': + resolution: {integrity: sha512-Dc5mPD8F5F/FS8i01syd7FTF6yB2fVthH/TRkjwJkzUK6EpoxHtqvZQP5Zwq80/5z19TWYHIg1KOHboCgVx/aQ==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [darwin] + + '@rolldown/binding-darwin-x64@1.2.4': + resolution: {integrity: sha512-fpDm4oBo6SqLvWUYCmFhdde3U9KH2fRNNMeAnAPAIwxRL345xutL0EtEUcuoxsoazdJGv/MuDBQHlCDrtbvqOg==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [darwin] + + '@rolldown/binding-freebsd-x64@1.2.4': + resolution: {integrity: sha512-rSJoreDE/HoIzoaib6MTp5jQtCTdMHKIvItAKT/ImS6Y6Ww76oUaeMyp4Vc/fAgd/ehji068IxetHXAnqUwN9A==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [freebsd] + + '@rolldown/binding-linux-arm-gnueabihf@1.2.4': + resolution: {integrity: sha512-/jm8OGHgn7oGaJu3i/qZI9spUGcJ+y/lk43ttQ/iO1tOd9NissG6o97bighBCiL+BKRngmcDuR6ikfwYdJmVuQ==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm] + os: [linux] + + '@rolldown/binding-linux-arm64-gnu@1.2.4': + resolution: {integrity: sha512-tIP06BeD9EqvECBrPZ+sqdPlYrT+aYaAiu1wYziVx5elRK/ftm33JxVDy2bXGbr6J0CrtirCkR87/X5a2euEng==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [linux] + libc: [glibc] + + '@rolldown/binding-linux-arm64-musl@1.2.4': + resolution: {integrity: sha512-Ql1Q0EQqVThvn9VAVlwNzsUvbSFtCMGjLpRRi4pk5i7NZZ4n5ISiLMjHYtus4VQ2PvkSw24zyaCVsiS+sXPj1w==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [linux] + libc: [musl] + + '@rolldown/binding-linux-ppc64-gnu@1.2.4': + resolution: {integrity: sha512-GjbjXD4XXfN19D0LZNbmiCBUoDiRACsYHr0yaIbbn8aFsXjHZifcYqu/W5Er5X2X990WjHXFrxarn5chzItorQ==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [ppc64] + os: [linux] + libc: [glibc] + + '@rolldown/binding-linux-s390x-gnu@1.2.4': + resolution: {integrity: sha512-p5WR0NOwaRmJ/B1b6IjEFLLivwEsf3PrdBIhRbhTCQisbo2SvHHpG4ELB/+FgQNnB88LTOF86upmJmbvZdQ2lw==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [s390x] + os: [linux] + libc: [glibc] + + '@rolldown/binding-linux-x64-gnu@1.2.4': + resolution: {integrity: sha512-4/GyVjmhR+Tc6HLJvwc1sOhPqAZtySiSMesOZyX6JQ5XBxoTDEMKQzvo07NIK6nTon/SivlZqvhzvuVBNQhObQ==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [linux] + libc: [glibc] + + '@rolldown/binding-linux-x64-musl@1.2.4': + resolution: {integrity: sha512-l9eeLsCNvPpmSXUej0etw/J1eqV0Jj1D5G/xG6YTijmE6dkv6E2QezgWbTfQk63v952DPqrjOCoiqxq7Bw0YUQ==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [linux] + libc: [musl] + + '@rolldown/binding-openharmony-arm64@1.2.4': + resolution: {integrity: sha512-e0F355MSTMm3+UOqtV3L24gFUp2N5m1f8L/7d56deik6va+AXdrt9F8LbzGpeWGWRbZEDq4m8NVnJDeBtf9DZg==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [openharmony] + + '@rolldown/binding-win32-arm64-msvc@1.2.4': + resolution: {integrity: sha512-AWLi0uBRYh6QlE7OKhiz+phZC0qwtij2QZmhmOdsLdFn64m7oMpooE9ICE3lhm9xMb4SpDo2WbHcxX1iFLFtqw==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [win32] + + '@rolldown/binding-win32-x64-msvc@1.2.4': + resolution: {integrity: sha512-UwSDJOg3dqCAejWdxclJjCsh3Qq4vLYMDxmyHqo1btz3stK2VqgwNd3mm5tuIwzSlGIQ/1H9Hr+Zn09mrezNqQ==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [win32] + + '@rolldown/pluginutils@1.0.1': + resolution: {integrity: sha512-2j9bGt5Jh8hj+vPtgzPtl72j0yRxHAyumoo6TNfAjsLB04UtpSvPbPcDcBMxz7n+9CYB0c1GxQFxYRg2jimqGw==} + + '@rtsao/scc@1.1.0': + resolution: {integrity: sha512-zt6OdqaDoOnJ1ZYsCYGt9YmWzDXl4vQdKTyJev62gFhRGKdx7mcT54V9KIjg+d2wi9EXsPvAPKe7i7WjfVWB8g==} + + '@standard-schema/spec@1.1.0': + resolution: {integrity: sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w==} + + '@tweenjs/tween.js@23.1.3': + resolution: {integrity: sha512-vJmvvwFxYuGnF2axRtPYocag6Clbb5YS7kLL+SO/TeVFzHqDIWrNKYtcsPMibjDx9O+bu+psAy9NKfWklassUA==} + + '@types/chai@5.2.3': + resolution: {integrity: sha512-Mw558oeA9fFbv65/y4mHtXDs9bPnFMZAL/jxdPFUpOHHIXX91mcgEHbS5Lahr+pwZFR8A7GQleRWeI6cGFC2UA==} + + '@types/deep-eql@4.0.2': + resolution: {integrity: sha512-c9h9dVVMigMPc4bwTvC5dxqtqJZwQPePsWjPlpSOnojbor6pGqdk541lfA7AqFQr5pB1BRdq0juY9db81BwyFw==} + + '@types/estree@1.0.9': + resolution: {integrity: sha512-GhdPgy1el4/ImP05X05Uw4cw2/M93BCUmnEvWZNStlCzEKME4Fkk+YpoA5OiHNQmoS7Cafb8Xa3Pya8m1Qrzeg==} + + '@types/json-schema@7.0.15': + resolution: {integrity: sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==} + + '@types/json5@0.0.29': + resolution: {integrity: sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==} + + '@types/node@26.2.0': + resolution: {integrity: sha512-5IviulTZeRNp2vAJ514cc/HUlY5nZ9fCbq9DMyC52BrhFZACo3nI0R7qBxhQmo/d27NFe96ur/b7Wwxklda+kg==} + + '@types/react-reconciler@0.28.9': + resolution: {integrity: sha512-HHM3nxyUZ3zAylX8ZEyrDNd2XZOnQ0D5XfunJF5FLQnZbHHYq4UWvW1QfelQNXv1ICNkwYhfxjwfnqivYB6bFg==} + peerDependencies: + '@types/react': '*' + + '@types/react@19.2.18': + resolution: {integrity: sha512-AnzbBERsrLKtk2XSfTbYRLjQPdy116Sty4q+T+Bp3IC4l6jNBvreVPAHmpq9qhXQM7CXZPjLVmGMw9sy+hxQ3w==} + + '@types/stats.js@0.17.4': + resolution: {integrity: sha512-jIBvWWShCvlBqBNIZt0KAshWpvSjhkwkEu4ZUcASoAvhmrgAUI2t1dXrjSL4xXVLB4FznPrIsX3nKXFl/Dt4vA==} + + '@types/three@0.182.0': + resolution: {integrity: sha512-WByN9V3Sbwbe2OkWuSGyoqQO8Du6yhYaXtXLoA5FkKTUJorZ+yOHBZ35zUUPQXlAKABZmbYp5oAqpA4RBjtJ/Q==} + + '@types/webxr@0.5.24': + resolution: {integrity: sha512-h8fgEd/DpoS9CBrjEQXR+dIDraopAEfu4wYVNY2tEPwk60stPWhvZMf4Foo5FakuQ7HFZoa8WceaWFervK2Ovg==} + + '@typescript-eslint/eslint-plugin@8.67.0': + resolution: {integrity: sha512-Un7Heoyj65NREbKAyIrFxeM143NZpExWmy1Nep4DLeQOeLlTeumPjoNKnBrU5D5moWXbPJgRa5Uwcdu0faVNGQ==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + '@typescript-eslint/parser': ^8.67.0 + eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 + typescript: '>=4.8.4 <6.1.0' + + '@typescript-eslint/parser@8.67.0': + resolution: {integrity: sha512-fUBfTuuEulWqX6V8+O3PtScV01tzYYRUDTAirHFKoRAt7nOzoGiPt0M/bB47wWNy0coOOcgEwAMUtBpykMxl6w==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 + typescript: '>=4.8.4 <6.1.0' + + '@typescript-eslint/project-service@8.67.0': + resolution: {integrity: sha512-cvE8c7ulYeXN9fYuszhCeCsbzyVEXuhrRCybnBre7TUmqb5nRmBfQAwCj0O3WJFDeyAZt4VYv51vMCC9LHSdYw==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + typescript: '>=4.8.4 <6.1.0' + + '@typescript-eslint/scope-manager@8.67.0': + resolution: {integrity: sha512-EgvsleTwS4E+WzzSvem8fAUubLwatMNF1B5hHSLQxcvs7q2dtRhGyujHwLJSYlG41niJ7GP24Aha2+0mb1b2kg==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@typescript-eslint/tsconfig-utils@8.67.0': + resolution: {integrity: sha512-vV+LUSv5njUWsknE71fqKTlXUva+R76SaeORd6Zojcunk/6DvKFXONU3BrAs2H49mbygUXt6gbYunzwqNwlhdg==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + typescript: '>=4.8.4 <6.1.0' + + '@typescript-eslint/type-utils@8.67.0': + resolution: {integrity: sha512-aVWDXbRmdXO9siTfX4ditQI1T9+zVcNazT48EJCD0v40/9RIFoUgZ05CmGEq9H2gixRpjUn/iplwvlcvutJW/Q==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 + typescript: '>=4.8.4 <6.1.0' + + '@typescript-eslint/types@8.67.0': + resolution: {integrity: sha512-sBtgslww8nsMYUjhdPBiSyUqSzT8uR6g93A2QXnQC8+cGdjz0CyaOdqHDRJb1AtORbZCNUJBBeFA/tNR2uQmww==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@typescript-eslint/typescript-estree@8.67.0': + resolution: {integrity: sha512-EKQBCE9yNlRJYm7jdTW5AhDacDUmSwQb0FAJAmK2EKYrNXIsa2vxcSZx6PvJ/dEdI6lS+Y9W+EXckLj0iPFGcw==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + typescript: '>=4.8.4 <6.1.0' + + '@typescript-eslint/utils@8.67.0': + resolution: {integrity: sha512-U9D1FdwEWBwok3hxxSdhclMb0twvt9QnjIQ0VfQ1AiX2epnpSgv2ubVDsayOFyY8K6FX+AQ7E0FKWVG3iKsj1A==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 + typescript: '>=4.8.4 <6.1.0' + + '@typescript-eslint/visitor-keys@8.67.0': + resolution: {integrity: sha512-fkv8dHRDqfGtTHuJeebdrQ7cX6Ad4WAS00rgHh9UGvMycF1mjBfsxry1XsLIFhWZ6Judlh6UdzK+TYlbpCXgnA==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@vitest/expect@4.1.10': + resolution: {integrity: sha512-YsCn+qAk1GWjQOWFEsEcL2gNQ0zmVmQu3T03qP6UyjhtmdtwtbuI+DASn/7iQB3HGTXkdBwGddzxPlmiql5vlA==} + + '@vitest/mocker@4.1.10': + resolution: {integrity: sha512-v0xaezt+DKEmKfaxg133ldzADrwLGd7Ze1MfQQTYfvs8OqZIwbxyxaYURivwV7sWy5fqn3rH5uOrSp07bp44Ow==} + peerDependencies: + msw: ^2.4.9 + vite: ^6.0.0 || ^7.0.0 || ^8.0.0 + peerDependenciesMeta: + msw: + optional: true + vite: + optional: true + + '@vitest/pretty-format@4.1.10': + resolution: {integrity: sha512-W1HsjSH4MXQ9YfmmhLAoIYf1HRfekQCGngeIgcei6MP5QQGWUe0gkopdZQaVCFO+JDJMrAJGwa5pRpNpvy4P8Q==} + + '@vitest/runner@4.1.10': + resolution: {integrity: sha512-IKI6kpIH+LmpROplyLwBBaCfMgOZOMsygVa6BARD6ahA04VRuJSa6OaVG7kRvSEMD870Vd91rSSw0eegtWyLGg==} + + '@vitest/snapshot@4.1.10': + resolution: {integrity: sha512-xRkfOT1qpTAi/Ti4Y1LtfRc3kEuqxGw59eN2jN9pRWMtS/XDevekhcFSqvQqjUNGksfjMJu3Y+oJ+4Ypn2OaJw==} + + '@vitest/spy@4.1.10': + resolution: {integrity: sha512-PLf/Ugvoq5wO/b4rwYCR1h2PSIdXz7wnkQFMiUpLdtM7l6pqVFcQIBEHyT1+l+cj7mNwAfZHzqXqDyjvOuwbDw==} + + '@vitest/utils@4.1.10': + resolution: {integrity: sha512-fy9am/HWxbaGt/Sawrp90vt6Y6jQwf1RX77cz3uwoJwJVMli/e1IEwRPnMNJ7vKfPTwo0diXifkpPvwH9v7nGA==} + + '@webgpu/types@0.1.71': + resolution: {integrity: sha512-mMy8/ODcKhab808co15eW+yN+HgXoQxRQHTiBV9Mrvl1r0ufnid7YOcI+gi4eUWSWl9ezD6TW2KXccrL8HCh2A==} + + acorn-jsx@5.3.2: + resolution: {integrity: sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==} + peerDependencies: + acorn: ^6.0.0 || ^7.0.0 || ^8.0.0 + + acorn@8.18.0: + resolution: {integrity: sha512-lGq+9yr1/GuAWaVYIHRjvvySG5/4VfKIvC8EWxStPdcDh/Ka7FG3twP6v4d5BkravUilhIAsG4Qj83t02LWUPQ==} + engines: {node: '>=0.4.0'} + hasBin: true + + ajv@6.15.0: + resolution: {integrity: sha512-fgFx7Hfoq60ytK2c7DhnF8jIvzYgOMxfugjLOSMHjLIPgenqa7S7oaagATUq99mV6IYvN2tRmC0wnTYX6iPbMw==} + + ansi-styles@4.3.0: + resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==} + engines: {node: '>=8'} + + argparse@2.0.1: + resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==} + + array-buffer-byte-length@1.0.2: + resolution: {integrity: sha512-LHE+8BuR7RYGDKvnrmcuSq3tDcKv9OFEXQt/HpbZhY7V6h0zlUXutnAD82GiFx9rdieCMjkvtcsPqBwgUl1Iiw==} + engines: {node: '>= 0.4'} + + array-includes@3.1.9: + resolution: {integrity: sha512-FmeCCAenzH0KH381SPT5FZmiA/TmpndpcaShhfgEN9eCVjnFBqq3l1xrI42y8+PPLI6hypzou4GXw00WHmPBLQ==} + engines: {node: '>= 0.4'} + + array.prototype.findlast@1.2.5: + resolution: {integrity: sha512-CVvd6FHg1Z3POpBLxO6E6zr+rSKEQ9L6rZHAaY7lLfhKsWYUBBOuMs0e9o24oopj6H+geRCX0YJ+TJLBK2eHyQ==} + engines: {node: '>= 0.4'} + + array.prototype.findlastindex@1.2.6: + resolution: {integrity: sha512-F/TKATkzseUExPlfvmwQKGITM3DGTK+vkAsCZoDc5daVygbJBnjEUCbgkAvVFsgfXfX4YIqZ/27G3k3tdXrTxQ==} + engines: {node: '>= 0.4'} + + array.prototype.flat@1.3.3: + resolution: {integrity: sha512-rwG/ja1neyLqCuGZ5YYrznA62D4mZXg0i1cIskIUKSiqF3Cje9/wXAls9B9s1Wa2fomMsIv8czB8jZcPmxCXFg==} + engines: {node: '>= 0.4'} + + array.prototype.flatmap@1.3.3: + resolution: {integrity: sha512-Y7Wt51eKJSyi80hFrJCePGGNo5ktJCslFuboqJsbf57CCPcm5zztluPlc4/aD8sWsKvlwatezpV4U1efk8kpjg==} + engines: {node: '>= 0.4'} + + array.prototype.tosorted@1.1.4: + resolution: {integrity: sha512-p6Fx8B7b7ZhL/gmUsAy0D15WhvDccw3mnGNbZpi3pmeJdxtWsj2jEaI4Y6oo3XiHfzuSgPwKc04MYt6KgvC/wA==} + engines: {node: '>= 0.4'} + + arraybuffer.prototype.slice@1.0.4: + resolution: {integrity: sha512-BNoCY6SXXPQ7gF2opIP4GBE+Xw7U+pHMYKuzjgCN3GwiaIR09UUeKfheyIry77QtrCBlC0KK0q5/TER/tYh3PQ==} + engines: {node: '>= 0.4'} + + assertion-error@2.0.1: + resolution: {integrity: sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==} + engines: {node: '>=12'} + + async-function@1.0.0: + resolution: {integrity: sha512-hsU18Ae8CDTR6Kgu9DYf0EbCr/a5iGL0rytQDobUcdpYOKokk8LEjVphnXkDkgpi0wYVsqrXuP0bZxJaTqdgoA==} + engines: {node: '>= 0.4'} + + available-typed-arrays@1.0.7: + resolution: {integrity: sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==} + engines: {node: '>= 0.4'} + + balanced-match@1.0.2: + resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} + + balanced-match@4.0.4: + resolution: {integrity: sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==} + engines: {node: 18 || 20 || >=22} + + base64-js@1.5.1: + resolution: {integrity: sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==} + + baseline-browser-mapping@2.11.14: + resolution: {integrity: sha512-JyJ954WzuIR8/FFzX0o5krdSTrBAkcCSRfWSleRsIHSWV+cZe2FI1PKggVkFke1hBldRs+LRxUczzE9iPmgZww==} + engines: {node: '>=6.0.0'} + hasBin: true + + brace-expansion@1.1.18: + resolution: {integrity: sha512-Edep/X9fGqVNmzKBVsDYIOtD+z1tuezV70LBjdCst9Tqu76lsnvRiZ6oTic1n+/BIwX6QDGAO94PN4N2SADvtw==} + + brace-expansion@5.0.9: + resolution: {integrity: sha512-ScQ4IuvIEF1TMlP7Zt+vjJ//9zlPb2SDcxWxM3bk8s6t6GGdJ7KO1dCcTidOPJKePW30LE/2cT7wCyPho9/Wxg==} + engines: {node: 20 || >=22} + + browserslist@4.28.8: + resolution: {integrity: sha512-V2NpofLblG64mfOtSgDhOJESZEGogzDMBv/q+W6oc4LXWP/q75eOXoOaaOu1EOadB9U4Bwx/e0yzbvwKH8zalA==} + engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7} + hasBin: true + + buffer@6.0.3: + resolution: {integrity: sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==} + + call-bind-apply-helpers@1.0.2: + resolution: {integrity: sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==} + engines: {node: '>= 0.4'} + + call-bind@1.0.9: + resolution: {integrity: sha512-a/hy+pNsFUTR+Iz8TCJvXudKVLAnz/DyeSUo10I5yvFDQJBFU2s9uqQpoSrJlroHUKoKqzg+epxyP9lqFdzfBQ==} + engines: {node: '>= 0.4'} + + call-bound@1.0.4: + resolution: {integrity: sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==} + engines: {node: '>= 0.4'} + + callsites@3.1.0: + resolution: {integrity: sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==} + engines: {node: '>=6'} + + caniuse-lite@1.0.30001809: + resolution: {integrity: sha512-xxWVywk6a6Arlk+hymeycyn/VgqEfLDxupvhH/xiY5SJ/18kmi9o6MiO320DCUzypORHLtvh0I4i04tUhCNHNQ==} + + chai@6.2.2: + resolution: {integrity: sha512-NUPRluOfOiTKBKvWPtSD4PhFvWCqOi0BGStNWs57X9js7XGTprSmFoz5F0tWhR4WPjNeR9jXqdC7/UpSJTnlRg==} + engines: {node: '>=18'} + + chalk@4.1.2: + resolution: {integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==} + engines: {node: '>=10'} + + color-convert@2.0.1: + resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==} + engines: {node: '>=7.0.0'} + + color-name@1.1.4: + resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==} + + concat-map@0.0.1: + resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==} + + convert-source-map@2.0.0: + resolution: {integrity: sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==} + + cross-spawn@7.0.6: + resolution: {integrity: sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==} + engines: {node: '>= 8'} + + csstype@3.2.3: + resolution: {integrity: sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==} + + data-view-buffer@1.0.2: + resolution: {integrity: sha512-EmKO5V3OLXh1rtK2wgXRansaK1/mtVdTUEiEI0W8RkvgT05kfxaH29PliLnpLP73yYO6142Q72QNa8Wx/A5CqQ==} + engines: {node: '>= 0.4'} + + data-view-byte-length@1.0.2: + resolution: {integrity: sha512-tuhGbE6CfTM9+5ANGf+oQb72Ky/0+s3xKUpHvShfiz2RxMFgFPjsXuRLBVMtvMs15awe45SRb83D6wH4ew6wlQ==} + engines: {node: '>= 0.4'} + + data-view-byte-offset@1.0.1: + resolution: {integrity: sha512-BS8PfmtDGnrgYdOonGZQdLZslWIeCGFP9tpan0hi1Co2Zr2NKADsvGYA8XxuG/4UWgJ6Cjtv+YJnB6MM69QGlQ==} + engines: {node: '>= 0.4'} + + debug@3.2.7: + resolution: {integrity: sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==} + peerDependencies: + supports-color: '*' + peerDependenciesMeta: + supports-color: + optional: true + + debug@4.4.3: + resolution: {integrity: sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==} + engines: {node: '>=6.0'} + peerDependencies: + supports-color: '*' + peerDependenciesMeta: + supports-color: + optional: true + + deep-is@0.1.4: + resolution: {integrity: sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==} + + define-data-property@1.1.4: + resolution: {integrity: sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==} + engines: {node: '>= 0.4'} + + define-properties@1.2.1: + resolution: {integrity: sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==} + engines: {node: '>= 0.4'} + + detect-libc@2.1.2: + resolution: {integrity: sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==} + engines: {node: '>=8'} + + doctrine@2.1.0: + resolution: {integrity: sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==} + engines: {node: '>=0.10.0'} + + dunder-proto@1.0.1: + resolution: {integrity: sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==} + engines: {node: '>= 0.4'} + + electron-to-chromium@1.5.406: + resolution: {integrity: sha512-hWH5ORBi3d0IipnMh7BN5GDTaAmrSSSWmznwt2zltdiRNEWoEQyTwF0FFSBxzHO7hLSRT6loQu3IQGV0wg/Tvg==} + + es-abstract-get@1.0.0: + resolution: {integrity: sha512-6PMWXpdhshVvFp+FoWYs1EvG1Nj0tvk0dZM+XcK0xMEM1czRVcP6ohqPWHy6qPagSpC8j4+p89WXlT+xXJs/fg==} + engines: {node: '>= 0.4'} + + es-abstract@1.24.2: + resolution: {integrity: sha512-2FpH9Q5i2RRwyEP1AylXe6nYLR5OhaJTZwmlcP0dL/+JCbgg7yyEo/sEK6HeGZRf3dFpWwThaRHVApXSkW3xeg==} + engines: {node: '>= 0.4'} + + es-define-property@1.0.1: + resolution: {integrity: sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==} + engines: {node: '>= 0.4'} + + es-errors@1.3.0: + resolution: {integrity: sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==} + engines: {node: '>= 0.4'} + + es-iterator-helpers@1.4.0: + resolution: {integrity: sha512-c/A0P0oxkACDc+cKWw8evLXK83oBKgn0qPOqCYT4x9uolpCIJAcYvJC9QYKNDRPsTeGyCrQ326jrvgZWdCdK5Q==} + engines: {node: '>= 0.4'} + + es-module-lexer@2.3.1: + resolution: {integrity: sha512-shc1dbU90Yl/xq1QrC7QRtfcwURZuVRfPhZbDoldJ1cn1gzDvBaBWlv0eFolj5+0znnPJz5TXLxsN77X/12KTA==} + + es-object-atoms@1.1.2: + resolution: {integrity: sha512-HWcBoN6NileqtSydK2FqHbS/LoDd2pqrnQHLyJzBj4kOp/ky2MWMN694xOfkK8/SnUsW2DH7EfyVlydKCsm1Zw==} + engines: {node: '>= 0.4'} + + es-set-tostringtag@2.1.0: + resolution: {integrity: sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==} + engines: {node: '>= 0.4'} + + es-shim-unscopables@1.1.0: + resolution: {integrity: sha512-d9T8ucsEhh8Bi1woXCf+TIKDIROLG5WCkxg8geBCbvk22kzwC5G2OnXVMO6FUsvQlgUUXQ2itephWDLqDzbeCw==} + engines: {node: '>= 0.4'} + + es-to-primitive@1.3.4: + resolution: {integrity: sha512-yPDz7wqpg1/mmHLmS3tcfTfbw5f1eryXvyghYBffGdERwe+mV7ZcWzTR8LR17Kvqt3qfPurjlonmnq3MKXIOXw==} + engines: {node: '>= 0.4'} + + escalade@3.2.0: + resolution: {integrity: sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==} + engines: {node: '>=6'} + + escape-string-regexp@4.0.0: + resolution: {integrity: sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==} + engines: {node: '>=10'} + + eslint-config-prettier@10.1.8: + resolution: {integrity: sha512-82GZUjRS0p/jganf6q1rEO25VSoHH0hKPCTrgillPjdI/3bgBhAE1QzHrHTizjpRvy6pGAvKjDJtk2pF9NDq8w==} + hasBin: true + peerDependencies: + eslint: '>=7.0.0' + + eslint-import-resolver-alias@1.1.2: + resolution: {integrity: sha512-WdviM1Eu834zsfjHtcGHtGfcu+F30Od3V7I9Fi57uhBEwPkjDcii7/yW8jAT+gOhn4P/vOxxNAXbFAKsrrc15w==} + engines: {node: '>= 4'} + peerDependencies: + eslint-plugin-import: '>=1.4.0' + + eslint-import-resolver-node@0.3.10: + resolution: {integrity: sha512-tRrKqFyCaKict5hOd244sL6EQFNycnMQnBe+j8uqGNXYzsImGbGUU4ibtoaBmv5FLwJwcFJNeg1GeVjQfbMrDQ==} + + eslint-module-utils@2.14.0: + resolution: {integrity: sha512-W2WCRZ9Dqntd+2u8jJcVMV2PKulc6RdLgUUoh/yQr3uB6lo/ZOeGx11sv60/8S4QFFKNslAlWhr9u0Ef7ZW6Ig==} + engines: {node: '>=4'} + peerDependencies: + '@typescript-eslint/parser': '*' + eslint: '*' + eslint-import-resolver-node: '*' + eslint-import-resolver-typescript: '*' + eslint-import-resolver-webpack: '*' + peerDependenciesMeta: + '@typescript-eslint/parser': + optional: true + eslint: + optional: true + eslint-import-resolver-node: + optional: true + eslint-import-resolver-typescript: + optional: true + eslint-import-resolver-webpack: + optional: true + + eslint-plugin-import@2.32.0: + resolution: {integrity: sha512-whOE1HFo/qJDyX4SnXzP4N6zOWn79WhnCUY/iDR0mPfQZO8wcYE4JClzI2oZrhBnnMUCBCHZhO6VQyoBU95mZA==} + engines: {node: '>=4'} + peerDependencies: + '@typescript-eslint/parser': '*' + eslint: ^2 || ^3 || ^4 || ^5 || ^6 || ^7.2.0 || ^8 || ^9 + peerDependenciesMeta: + '@typescript-eslint/parser': + optional: true + + eslint-plugin-prettier@5.5.6: + resolution: {integrity: sha512-ifetmTcxWfz+4qRW3pH/ujdTq2jQIj59AxJMIN26K5avYgU8dxycUETQonWiW+wPrYXA0j3Try0l1CnwVQtDqQ==} + engines: {node: ^14.18.0 || >=16.0.0} + peerDependencies: + '@types/eslint': '>=8.0.0' + eslint: '>=8.0.0' + eslint-config-prettier: '>= 7.0.0 <10.0.0 || >=10.1.0' + prettier: '>=3.0.0' + peerDependenciesMeta: + '@types/eslint': + optional: true + eslint-config-prettier: + optional: true + + eslint-plugin-react-hooks@7.1.1: + resolution: {integrity: sha512-f2I7Gw6JbvCexzIInuSbZpfdQ44D7iqdWX01FKLvrPgqxoE7oMj8clOfto8U6vYiz4yd5oKu39rRSVOe1zRu0g==} + engines: {node: '>=18'} + peerDependencies: + eslint: ^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0 || ^9.0.0 || ^10.0.0 + + eslint-plugin-react@7.37.5: + resolution: {integrity: sha512-Qteup0SqU15kdocexFNAJMvCJEfa2xUKNV4CC1xsVMrIIqEy3SQ/rqyxCWNzfrd3/ldy6HMlD2e0JDVpDg2qIA==} + engines: {node: '>=4'} + peerDependencies: + eslint: ^3 || ^4 || ^5 || ^6 || ^7 || ^8 || ^9.7 + + eslint-scope@8.4.0: + resolution: {integrity: sha512-sNXOfKCn74rt8RICKMvJS7XKV/Xk9kA7DyJr8mJik3S7Cwgy3qlkkmyS2uQB3jiJg6VNdZd/pDBJu0nvG2NlTg==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + eslint-visitor-keys@3.4.3: + resolution: {integrity: sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + + eslint-visitor-keys@4.2.1: + resolution: {integrity: sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + eslint-visitor-keys@5.0.1: + resolution: {integrity: sha512-tD40eHxA35h0PEIZNeIjkHoDR4YjjJp34biM0mDvplBe//mB+IHCqHDGV7pxF+7MklTvighcCPPZC7ynWyjdTA==} + engines: {node: ^20.19.0 || ^22.13.0 || >=24} + + eslint@9.39.5: + resolution: {integrity: sha512-DgZS62aPLXKlnxILS/AYCoRvHaZeXceIzlXPkkGGzJWSow1aEk0lbTlxUSlyjC8jcaKxAdOnTDz+o1JFSBsyjw==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + hasBin: true + peerDependencies: + jiti: '*' + peerDependenciesMeta: + jiti: + optional: true + + espree@10.4.0: + resolution: {integrity: sha512-j6PAQ2uUr79PZhBjP5C5fhl8e39FmRnOjsD5lGnWrFU8i2G776tBK7+nP8KuQUTTyAZUwfQqXAgrVH5MbH9CYQ==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + esquery@1.7.0: + resolution: {integrity: sha512-Ap6G0WQwcU/LHsvLwON1fAQX9Zp0A2Y6Y/cJBl9r/JbW90Zyg4/zbG6zzKa2OTALELarYHmKu0GhpM5EO+7T0g==} + engines: {node: '>=0.10'} + + esrecurse@4.3.0: + resolution: {integrity: sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==} + engines: {node: '>=4.0'} + + estraverse@5.3.0: + resolution: {integrity: sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==} + engines: {node: '>=4.0'} + + estree-walker@3.0.3: + resolution: {integrity: sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==} + + esutils@2.0.3: + resolution: {integrity: sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==} + engines: {node: '>=0.10.0'} + + expect-type@1.4.0: + resolution: {integrity: sha512-KfYbmpRm0VbLjEvVa9yGwCi9GI34xvi7A/HXYWQO65CSD2u3MczUJSuwXKFIxlGsgBQizV9q5J9NHj4VG0n+pA==} + engines: {node: '>=12.0.0'} + + fast-deep-equal@3.1.3: + resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==} + + fast-diff@1.3.0: + resolution: {integrity: sha512-VxPP4NqbUjj6MaAOafWeUn2cXWLcCtljklUtZf0Ind4XQ+QPtmA0b18zZy0jIQx+ExRVCR/ZQpBmik5lXshNsw==} + + fast-json-stable-stringify@2.1.0: + resolution: {integrity: sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==} + + fast-levenshtein@2.0.6: + resolution: {integrity: sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==} + + fdir@6.5.0: + resolution: {integrity: sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==} + engines: {node: '>=12.0.0'} + peerDependencies: + picomatch: ^3 || ^4 + peerDependenciesMeta: + picomatch: + optional: true + + fflate@0.8.3: + resolution: {integrity: sha512-tbZNuJrLwGUp3zshBtdy4W+ORxZuIh8a5ilyIEQDC5rY1f3U20JMry0Ll3WBzU58EZKsEuJFXhb5gwv8CsPvgA==} + + file-entry-cache@8.0.0: + resolution: {integrity: sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==} + engines: {node: '>=16.0.0'} + + find-up@5.0.0: + resolution: {integrity: sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==} + engines: {node: '>=10'} + + flat-cache@4.0.1: + resolution: {integrity: sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==} + engines: {node: '>=16'} + + flatted@3.4.4: + resolution: {integrity: sha512-5+ybhBZANEJxaH3X5evAFatUxLfEHSr7n6kYJ+1Qd0mUqr4eu9gIf6GDbWHf8RJijHrjjO8G+la14SlL2SeS1Q==} + + for-each@0.3.5: + resolution: {integrity: sha512-dKx12eRCVIzqCxFGplyFKJMPvLEWgmNtUrpTiJIR5u97zEhRG8ySrtboPHZXx7daLxQVrl643cTzbab2tkQjxg==} + engines: {node: '>= 0.4'} + + fsevents@2.3.3: + resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==} + engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} + os: [darwin] + + function-bind@1.1.2: + resolution: {integrity: sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==} + + function.prototype.name@1.2.0: + resolution: {integrity: sha512-jObKIik1P2QjPHP5nz5BaOtUlfgS0fWo8IUByNXkM+o+02sJOi94em77GwJKQSJ3gfPHdgzLNrHc1uokV4P/ew==} + engines: {node: '>= 0.4'} + + functions-have-names@1.2.3: + resolution: {integrity: sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==} + + generator-function@2.0.1: + resolution: {integrity: sha512-SFdFmIJi+ybC0vjlHN0ZGVGHc3lgE0DxPAT0djjVg+kjOnSqclqmj0KQ7ykTOLP6YxoqOvuAODGdcHJn+43q3g==} + engines: {node: '>= 0.4'} + + gensync@1.0.0-beta.2: + resolution: {integrity: sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==} + engines: {node: '>=6.9.0'} + + get-intrinsic@1.3.0: + resolution: {integrity: sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==} + engines: {node: '>= 0.4'} + + get-proto@1.0.1: + resolution: {integrity: sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==} + engines: {node: '>= 0.4'} + + get-symbol-description@1.1.0: + resolution: {integrity: sha512-w9UMqWwJxHNOvoNzSJ2oPF5wvYcvP7jUvYzhp67yEhTi17ZDBBC1z9pTdGuzjD+EFIqLSYRweZjqfiPzQ06Ebg==} + engines: {node: '>= 0.4'} + + glob-parent@6.0.2: + resolution: {integrity: sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==} + engines: {node: '>=10.13.0'} + + globals@14.0.0: + resolution: {integrity: sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==} + engines: {node: '>=18'} + + globals@17.11.0: + resolution: {integrity: sha512-Z2I8hM+PbJDXQDq3Icgpzv+mPdwr68iZUU9d5WW4FuXfDUQfkZaZuvjMv42/5crNyw154+9+VWXbYrUgDXbxNw==} + engines: {node: '>=18'} + + globalthis@1.0.4: + resolution: {integrity: sha512-DpLKbNU4WylpxJykQujfCcwYWiV/Jhm50Goo0wrVILAv5jOr9d+H+UR3PhSCD2rCCEIg0uc+G+muBTwD54JhDQ==} + engines: {node: '>= 0.4'} + + gopd@1.2.0: + resolution: {integrity: sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==} + engines: {node: '>= 0.4'} + + has-bigints@1.1.0: + resolution: {integrity: sha512-R3pbpkcIqv2Pm3dUwgjclDRVmWpTJW2DcMzcIhEXEx1oh/CEMObMm3KLmRJOdvhM7o4uQBnwr8pzRK2sJWIqfg==} + engines: {node: '>= 0.4'} + + has-flag@4.0.0: + resolution: {integrity: sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==} + engines: {node: '>=8'} + + has-property-descriptors@1.0.2: + resolution: {integrity: sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==} + + has-proto@1.2.0: + resolution: {integrity: sha512-KIL7eQPfHQRC8+XluaIw7BHUwwqL19bQn4hzNgdr+1wXoU0KKj6rufu47lhY7KbJR2C6T6+PfyN0Ea7wkSS+qQ==} + engines: {node: '>= 0.4'} + + has-symbols@1.1.0: + resolution: {integrity: sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==} + engines: {node: '>= 0.4'} + + has-tostringtag@1.0.2: + resolution: {integrity: sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==} + engines: {node: '>= 0.4'} + + hasown@2.0.4: + resolution: {integrity: sha512-T2UbfbBEF32wiepXIsMlTW9+dDYC6wMh/t/vYA4tuOMKqWz/n3vr1NFSxQiyP+zk2mXsoMA/i/7qV6LKut1t1A==} + engines: {node: '>= 0.4'} + + hermes-estree@0.25.1: + resolution: {integrity: sha512-0wUoCcLp+5Ev5pDW2OriHC2MJCbwLwuRx+gAqMTOkGKJJiBCLjtrvy4PWUGn6MIVefecRpzoOZ/UV6iGdOr+Cw==} + + hermes-parser@0.25.1: + resolution: {integrity: sha512-6pEjquH3rqaI6cYAXYPcz9MS4rY6R4ngRgrgfDshRptUZIc3lw0MCIJIGDj9++mfySOuPTHB4nrSW99BCvOPIA==} + + ieee754@1.2.1: + resolution: {integrity: sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==} + + ignore@5.3.2: + resolution: {integrity: sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==} + engines: {node: '>= 4'} + + ignore@7.0.6: + resolution: {integrity: sha512-BAg6QkE8W+TuQLrrw0Ugr7HegXduRuuj8/ti2kSOc+jz1dmx8/WNcjr6XGnq5YpDWxFwwaavqD0+jIUOKelTsw==} + engines: {node: '>= 4'} + + import-fresh@3.3.1: + resolution: {integrity: sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==} + engines: {node: '>=6'} + + imurmurhash@0.1.4: + resolution: {integrity: sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==} + engines: {node: '>=0.8.19'} + + internal-slot@1.1.0: + resolution: {integrity: sha512-4gd7VpWNQNB4UKKCFFVcp1AVv+FMOgs9NKzjHKusc8jTMhd5eL1NqQqOpE0KzMds804/yHlglp3uxgluOqAPLw==} + engines: {node: '>= 0.4'} + + is-array-buffer@3.0.5: + resolution: {integrity: sha512-DDfANUiiG2wC1qawP66qlTugJeL5HyzMpfr8lLK+jMQirGzNod0B12cFB/9q838Ru27sBwfw78/rdoU7RERz6A==} + engines: {node: '>= 0.4'} + + is-async-function@2.1.1: + resolution: {integrity: sha512-9dgM/cZBnNvjzaMYHVoxxfPj2QXt22Ev7SuuPrs+xav0ukGB0S6d4ydZdEiM48kLx5kDV+QBPrpVnFyefL8kkQ==} + engines: {node: '>= 0.4'} + + is-bigint@1.1.0: + resolution: {integrity: sha512-n4ZT37wG78iz03xPRKJrHTdZbe3IicyucEtdRsV5yglwc3GyUfbAfpSeD0FJ41NbUNSt5wbhqfp1fS+BgnvDFQ==} + engines: {node: '>= 0.4'} + + is-boolean-object@1.2.2: + resolution: {integrity: sha512-wa56o2/ElJMYqjCjGkXri7it5FbebW5usLw/nPmCMs5DeZ7eziSYZhSmPRn0txqeW4LnAmQQU7FgqLpsEFKM4A==} + engines: {node: '>= 0.4'} + + is-callable@1.2.7: + resolution: {integrity: sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==} + engines: {node: '>= 0.4'} + + is-core-module@2.16.2: + resolution: {integrity: sha512-evOr8xfXKxE6qSR0hSXL2r3sd7ALj8+7jQEUvPYcm5sgZFdJ+AYzT6yNmJenvIYQBgIGwfwz08sL8zoL7yq2BA==} + engines: {node: '>= 0.4'} + + is-data-view@1.0.2: + resolution: {integrity: sha512-RKtWF8pGmS87i2D6gqQu/l7EYRlVdfzemCJN/P3UOs//x1QE7mfhvzHIApBTRf7axvT6DMGwSwBXYCT0nfB9xw==} + engines: {node: '>= 0.4'} + + is-date-object@1.1.0: + resolution: {integrity: sha512-PwwhEakHVKTdRNVOw+/Gyh0+MzlCl4R6qKvkhuvLtPMggI1WAHt9sOwZxQLSGpUaDnrdyDsomoRgNnCfKNSXXg==} + engines: {node: '>= 0.4'} + + is-document.all@1.0.0: + resolution: {integrity: sha512-+XSoyS05OdBbhFuELhgTCpFNHkpBOJqtsZfUFFpe5QTw+9Sjbh8zitxhQkYAo6wV7e1Vb8cAPvpCk9jGam/82g==} + engines: {node: '>= 0.4'} + + is-extglob@2.1.1: + resolution: {integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==} + engines: {node: '>=0.10.0'} + + is-finalizationregistry@1.1.1: + resolution: {integrity: sha512-1pC6N8qWJbWoPtEjgcL2xyhQOP491EQjeUo3qTKcmV8YSDDJrOepfG8pcC7h/QgnQHYSv0mJ3Z/ZWxmatVrysg==} + engines: {node: '>= 0.4'} + + is-generator-function@1.1.2: + resolution: {integrity: sha512-upqt1SkGkODW9tsGNG5mtXTXtECizwtS2kA161M+gJPc1xdb/Ax629af6YrTwcOeQHbewrPNlE5Dx7kzvXTizA==} + engines: {node: '>= 0.4'} + + is-glob@4.0.3: + resolution: {integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==} + engines: {node: '>=0.10.0'} + + is-map@2.0.3: + resolution: {integrity: sha512-1Qed0/Hr2m+YqxnM09CjA2d/i6YZNfF6R2oRAOj36eUdS6qIV/huPJNSEpKbupewFs+ZsJlxsjjPbc0/afW6Lw==} + engines: {node: '>= 0.4'} + + is-negative-zero@2.0.3: + resolution: {integrity: sha512-5KoIu2Ngpyek75jXodFvnafB6DJgr3u8uuK0LEZJjrU19DrMD3EVERaR8sjz8CCGgpZvxPl9SuE1GMVPFHx1mw==} + engines: {node: '>= 0.4'} + + is-number-object@1.1.1: + resolution: {integrity: sha512-lZhclumE1G6VYD8VHe35wFaIif+CTy5SJIi5+3y4psDgWu4wPDoBhF8NxUOinEc7pHgiTsT6MaBb92rKhhD+Xw==} + engines: {node: '>= 0.4'} + + is-regex@1.2.1: + resolution: {integrity: sha512-MjYsKHO5O7mCsmRGxWcLWheFqN9DJ/2TmngvjKXihe6efViPqc274+Fx/4fYj/r03+ESvBdTXK0V6tA3rgez1g==} + engines: {node: '>= 0.4'} + + is-set@2.0.3: + resolution: {integrity: sha512-iPAjerrse27/ygGLxw+EBR9agv9Y6uLeYVJMu+QNCoouJ1/1ri0mGrcWpfCqFZuzzx3WjtwxG098X+n4OuRkPg==} + engines: {node: '>= 0.4'} + + is-shared-array-buffer@1.0.4: + resolution: {integrity: sha512-ISWac8drv4ZGfwKl5slpHG9OwPNty4jOWPRIhBpxOoD+hqITiwuipOQ2bNthAzwA3B4fIjO4Nln74N0S9byq8A==} + engines: {node: '>= 0.4'} + + is-string@1.1.1: + resolution: {integrity: sha512-BtEeSsoaQjlSPBemMQIrY1MY0uM6vnS1g5fmufYOtnxLGUZM2178PKbhsk7Ffv58IX+ZtcvoGwccYsh0PglkAA==} + engines: {node: '>= 0.4'} + + is-symbol@1.1.1: + resolution: {integrity: sha512-9gGx6GTtCQM73BgmHQXfDmLtfjjTUDSyoxTCbp5WtoixAhfgsDirWIcVQ/IHpvI5Vgd5i/J5F7B9cN/WlVbC/w==} + engines: {node: '>= 0.4'} + + is-typed-array@1.1.15: + resolution: {integrity: sha512-p3EcsicXjit7SaskXHs1hA91QxgTw46Fv6EFKKGS5DRFLD8yKnohjF3hxoju94b/OcMZoQukzpPpBE9uLVKzgQ==} + engines: {node: '>= 0.4'} + + is-weakmap@2.0.2: + resolution: {integrity: sha512-K5pXYOm9wqY1RgjpL3YTkF39tni1XajUIkawTLUo9EZEVUFga5gSQJF8nNS7ZwJQ02y+1YCNYcMh+HIf1ZqE+w==} + engines: {node: '>= 0.4'} + + is-weakref@1.1.1: + resolution: {integrity: sha512-6i9mGWSlqzNMEqpCp93KwRS1uUOodk2OJ6b+sq7ZPDSy2WuI5NFIxp/254TytR8ftefexkWn5xNiHUNpPOfSew==} + engines: {node: '>= 0.4'} + + is-weakset@2.0.4: + resolution: {integrity: sha512-mfcwb6IzQyOKTs84CQMrOwW4gQcaTOAWJ0zzJCl2WSPDrWk/OzDaImWFH3djXhb24g4eudZfLRozAvPGw4d9hQ==} + engines: {node: '>= 0.4'} + + isarray@2.0.5: + resolution: {integrity: sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==} + + isexe@2.0.0: + resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==} + + iterator.prototype@1.1.5: + resolution: {integrity: sha512-H0dkQoCa3b2VEeKQBOxFph+JAbcrQdE7KC0UkqwpLmv2EC4P41QXP+rqo9wYodACiG5/WM5s9oDApTU8utwj9g==} + engines: {node: '>= 0.4'} + + its-fine@2.0.0: + resolution: {integrity: sha512-KLViCmWx94zOvpLwSlsx6yOCeMhZYaxrJV87Po5k/FoZzcPSahvK5qJ7fYhS61sZi5ikmh2S3Hz55A2l3U69ng==} + peerDependencies: + react: ^19.0.0 + + js-tokens@4.0.0: + resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==} + + js-yaml@4.3.1: + resolution: {integrity: sha512-CY6crGq313MX8GkwvB7tzgp99vjQxY1++5y10/BKN/GUfHqWaOGQMNZkBvqSzsZKWk/ijwHlWzzkLulsGHhjWQ==} + hasBin: true + + jsesc@3.1.0: + resolution: {integrity: sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==} + engines: {node: '>=6'} + hasBin: true + + json-buffer@3.0.1: + resolution: {integrity: sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==} + + json-schema-traverse@0.4.1: + resolution: {integrity: sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==} + + json-stable-stringify-without-jsonify@1.0.1: + resolution: {integrity: sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==} + + json5@1.0.2: + resolution: {integrity: sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA==} + hasBin: true + + json5@2.2.3: + resolution: {integrity: sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==} + engines: {node: '>=6'} + hasBin: true + + jsx-ast-utils@3.3.5: + resolution: {integrity: sha512-ZZow9HBI5O6EPgSJLUb8n2NKgmVWTwCvHGwFuJlMjvLFqlGG6pjirPhtdsseaLZjSibD8eegzmYpUZwoIlj2cQ==} + engines: {node: '>=4.0'} + + keyv@4.5.4: + resolution: {integrity: sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==} + + levn@0.4.1: + resolution: {integrity: sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==} + engines: {node: '>= 0.8.0'} + + lightningcss-android-arm64@1.33.0: + resolution: {integrity: sha512-gEpRTalKdosp4Bb8qWtc2iOgE5SeIHlpS1up9bFq2wAyYhl1UdTObYiHe98zEM9SQvSoqQZ1IQD0JNpg3Ml5pg==} + engines: {node: '>= 12.0.0'} + cpu: [arm64] + os: [android] + + lightningcss-darwin-arm64@1.33.0: + resolution: {integrity: sha512-Sciaz8eenNTKn9b3t7+xr0ipTp9YxKQY4npwQ3mrRuL0BAVHBLyZxofhaKBAVtzmtRZ/zTyo0/to4B1uWG/Djg==} + engines: {node: '>= 12.0.0'} + cpu: [arm64] + os: [darwin] + + lightningcss-darwin-x64@1.33.0: + resolution: {integrity: sha512-Z5UPAxzrjlWNNyGy6i65cJzzvgJ5D3T6wMvs+gWpY9d7qRhANrxqAp6LhxIgZhWEw18RfJTGcRxjuLIBr+m8XQ==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [darwin] + + lightningcss-freebsd-x64@1.33.0: + resolution: {integrity: sha512-QQM/Ti/hQajJwCY+RiWuCZ9sdtI/XQk7nDK5vC8kkdwixezOlDgvDx7+RT+QjK6FcFT4MpsuoBnHIo/O3StRRg==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [freebsd] + + lightningcss-linux-arm-gnueabihf@1.33.0: + resolution: {integrity: sha512-N7FVBe6iS24MlM6R/4RBTxGhQheZGs7tiQ9U32UtF75NzP5Q7xWPRqLBCKxlRQRk3rY1jCIPLzx7WzOhuUIRLQ==} + engines: {node: '>= 12.0.0'} + cpu: [arm] + os: [linux] + + lightningcss-linux-arm64-gnu@1.33.0: + resolution: {integrity: sha512-j2v/itmy4HlNxlc6voKXYgBqNi0Ng2LShg4z7GufpEgs05P+2suBVyi9I6YHq5uoVFx9ETin3eCEhLVyXGQnKg==} + engines: {node: '>= 12.0.0'} + cpu: [arm64] + os: [linux] + libc: [glibc] + + lightningcss-linux-arm64-musl@1.33.0: + resolution: {integrity: sha512-yiO5ROMuYQgXbC60yjZU5CYSFZGKXL0HFATXt9mHJn1+zW55oCtMI9NfcVhYLMFDL7gV7oBPon/EmMMGg2OvtQ==} + engines: {node: '>= 12.0.0'} + cpu: [arm64] + os: [linux] + libc: [musl] + + lightningcss-linux-x64-gnu@1.33.0: + resolution: {integrity: sha512-ar+Ju7LmcN0Jo4FpL4hpFybwNG9/3A/Br5KW2n2jyODg3MEZXaDYADdemoNS+BDNfMgKvylJLj4S5tyRActuAg==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [linux] + libc: [glibc] + + lightningcss-linux-x64-musl@1.33.0: + resolution: {integrity: sha512-RYiYbkokw0trfKqqzfF55lginwEPrD3OJDfTuJzFs1MK6iFnDenaz1fqLLtX4ITG3OktJQXOeTaw1awrBAlZPw==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [linux] + libc: [musl] + + lightningcss-win32-arm64-msvc@1.33.0: + resolution: {integrity: sha512-1K+MPfLSFVpphzpdbfkhlWk6wBrTObBzS2T6db10PNOZgR9GoVsAWzwNyuhUYYbTp23j+4RrncfujZ4uAzXvwA==} + engines: {node: '>= 12.0.0'} + cpu: [arm64] + os: [win32] + + lightningcss-win32-x64-msvc@1.33.0: + resolution: {integrity: sha512-OlEICDx/Xl0FqSp4bry8zFnCvGpig3Gl4gCquvYwHuqJKEC1+n9NgDniFvqHGmMv1ZkqDJrDqKKSykTDX+ehuA==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [win32] + + lightningcss@1.33.0: + resolution: {integrity: sha512-WkUDrojuJs0xkgGf2udWxa3yGBRxPtxUkB79i6aCZLRgc7PM8fZe9TosfPDcvEpQZbuFASnHYmRLBLUbmLOIIA==} + engines: {node: '>= 12.0.0'} + + locate-path@6.0.0: + resolution: {integrity: sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==} + engines: {node: '>=10'} + + lodash.merge@4.6.2: + resolution: {integrity: sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==} + + loose-envify@1.4.0: + resolution: {integrity: sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==} + hasBin: true + + lru-cache@5.1.1: + resolution: {integrity: sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==} + + maath@0.10.8: + resolution: {integrity: sha512-tRvbDF0Pgqz+9XUa4jjfgAQ8/aPKmQdWXilFu2tMy4GWj4NOsx99HlULO4IeREfbO3a0sA145DZYyvXPkybm0g==} + peerDependencies: + '@types/three': '>=0.134.0' + three: '>=0.134.0' + + magic-string@0.30.21: + resolution: {integrity: sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==} + + math-intrinsics@1.1.0: + resolution: {integrity: sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==} + engines: {node: '>= 0.4'} + + meshoptimizer@0.22.0: + resolution: {integrity: sha512-IebiK79sqIy+E4EgOr+CAw+Ke8hAspXKzBd0JdgEmPHiAwmvEj2S4h1rfvo+o/BnfEYd/jAOg5IeeIjzlzSnDg==} + + minimatch@10.2.6: + resolution: {integrity: sha512-vpLQEs+VLCr1nU0BXS07maYoFwlDAH0gngQuuttxIwutDFEMHq2blX+8vpgxDdK3J1PwjCJiep77OitTZ4Ll1A==} + engines: {node: 18 || 20 || >=22} + + minimatch@3.1.5: + resolution: {integrity: sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==} + + minimist@1.2.8: + resolution: {integrity: sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==} + + ms@2.1.3: + resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==} + + n8ao@2.0.1: + resolution: {integrity: sha512-MoP1QKUdj4/3cKEnhFl917t0eQwWGER1l43ckz5mNOPyYWGRDGQbVoTOQz/VBvy01in6QVNG+AL6Xxx5AjZBYA==} + peerDependencies: + postprocessing: '>=6.30.0' + three: '>=0.137' + + nanoid@3.3.18: + resolution: {integrity: sha512-DTg4MJbGMWkfi6VZFdNt2/caMbQy4Ou+Op/hJQvGEWcnVfoA1QA+xzRKAzw9jD6+GVOOeYr/mIcuDSdug6F6+w==} + engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} + hasBin: true + + natural-compare@1.4.0: + resolution: {integrity: sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==} + + node-exports-info@1.6.2: + resolution: {integrity: sha512-kXs9Go0cah0qHVV2v389IXQLdLCeE1xfFtjOAF+iobu0OIoG1pje8At2vMHyaPMiPMnG/LWP50twML21eMcAag==} + engines: {node: '>= 0.4'} + + node-releases@2.0.53: + resolution: {integrity: sha512-D9UOmYG3UH1V+ENW56t5QXBwJw1YEY18ruVeus89Rw+SyIgjPkCO84bRzO3uNIYosJbNwiabWVn48o3uJLjxFQ==} + engines: {node: '>=18'} + + object-assign@4.1.1: + resolution: {integrity: sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==} + engines: {node: '>=0.10.0'} + + object-inspect@1.13.4: + resolution: {integrity: sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==} + engines: {node: '>= 0.4'} + + object-keys@1.1.1: + resolution: {integrity: sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==} + engines: {node: '>= 0.4'} + + object.assign@4.1.7: + resolution: {integrity: sha512-nK28WOo+QIjBkDduTINE4JkF/UJJKyf2EJxvJKfblDpyg0Q+pkOHNTL0Qwy6NP6FhE/EnzV73BxxqcJaXY9anw==} + engines: {node: '>= 0.4'} + + object.entries@1.1.9: + resolution: {integrity: sha512-8u/hfXFRBD1O0hPUjioLhoWFHRmt6tKA4/vZPyckBr18l1KE9uHrFaFaUi8MDRTpi4uak2goyPTSNJLXX2k2Hw==} + engines: {node: '>= 0.4'} + + object.fromentries@2.0.8: + resolution: {integrity: sha512-k6E21FzySsSK5a21KRADBd/NGneRegFO5pLHfdQLpRDETUNJueLXs3WCzyQ3tFRDYgbq3KHGXfTbi2bs8WQ6rQ==} + engines: {node: '>= 0.4'} + + object.groupby@1.0.3: + resolution: {integrity: sha512-+Lhy3TQTuzXI5hevh8sBGqbmurHbbIjAi0Z4S63nthVLmLxfbj4T54a4CfZrXIrt9iP4mVAPYMo/v99taj3wjQ==} + engines: {node: '>= 0.4'} + + object.values@1.2.1: + resolution: {integrity: sha512-gXah6aZrcUxjWg2zR2MwouP2eHlCBzdV4pygudehaKXSGW4v2AsRQUK+lwwXhii6KFZcunEnmSUoYp5CXibxtA==} + engines: {node: '>= 0.4'} + + obug@2.1.4: + resolution: {integrity: sha512-4a+OsYv9UktOJKE+l1A4OufDgdRF9PifWj+tJnHURo/P+WOxpG4GzUFL9qCalmWauao6ogiG+QvnCovwPoyAWA==} + engines: {node: '>=12.20.0'} + + optionator@0.9.4: + resolution: {integrity: sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==} + engines: {node: '>= 0.8.0'} + + own-keys@1.0.2: + resolution: {integrity: sha512-19YVAg7T+WTrxggPukVq7DjTv6+PJ867TmhCvBsYwmbFCsZd344rq2Ld1p0wo8f8Qrrhgp82c6FJRqdXWtSEhg==} + engines: {node: '>= 0.4'} + + p-limit@3.1.0: + resolution: {integrity: sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==} + engines: {node: '>=10'} + + p-locate@5.0.0: + resolution: {integrity: sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==} + engines: {node: '>=10'} + + parent-module@1.0.1: + resolution: {integrity: sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==} + engines: {node: '>=6'} + + path-exists@4.0.0: + resolution: {integrity: sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==} + engines: {node: '>=8'} + + path-key@3.1.1: + resolution: {integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==} + engines: {node: '>=8'} + + path-parse@1.0.7: + resolution: {integrity: sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==} + + pathe@2.0.3: + resolution: {integrity: sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==} + + picocolors@1.1.1: + resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==} + + picomatch@4.0.5: + resolution: {integrity: sha512-RvwwcruNjI1ncT5xRakeyS9Lf8lcItv34KD+aif+VH9kduAyfYBipGh12274xtenIPZ119/R9BdTBa8gAwSh0A==} + engines: {node: '>=12'} + + possible-typed-array-names@1.1.0: + resolution: {integrity: sha512-/+5VFTchJDoVj3bhoqi6UeymcD00DAwb1nJwamzPvHEszJ4FpF6SNNbUbOS8yI56qHzdV8eK0qEfOSiodkTdxg==} + engines: {node: '>= 0.4'} + + postcss@8.5.26: + resolution: {integrity: sha512-u82N74LFzG8ca+dD8puPnplTXoGH4fTPpVGuIbt36G3qvNlkvfD0lEAZSxaly3KX8TS/L1A1gsCEmvKmBcVbkQ==} + engines: {node: ^10 || ^12 || >=14} + + postprocessing@6.39.4: + resolution: {integrity: sha512-oAS/PjAbc/xT3OzjUrGsorJ4J064XwhVD2t0OwKLP/E8QwDMUJ0oOv6ZI1SYMtLchv4g0ySEx+JcLy92+48vlA==} + peerDependencies: + three: '>= 0.168.0 < 0.186.0' + + prelude-ls@1.2.1: + resolution: {integrity: sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==} + engines: {node: '>= 0.8.0'} + + prettier-linter-helpers@1.0.1: + resolution: {integrity: sha512-SxToR7P8Y2lWmv/kTzVLC1t/GDI2WGjMwNhLLE9qtH8Q13C+aEmuRlzDst4Up4s0Wc8sF2M+J57iB3cMLqftfg==} + engines: {node: '>=6.0.0'} + + prettier@3.9.6: + resolution: {integrity: sha512-OpN0zzVdiaiAhxpuuj5efpIS4sY9j7bY6uR5mnj5yPzGkdkjNKSJeUThPb60Jw29QuAZgA4o+/iB49kFiaBX6g==} + engines: {node: '>=14'} + hasBin: true + + prop-types@15.8.1: + resolution: {integrity: sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==} + + punycode@2.3.1: + resolution: {integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==} + engines: {node: '>=6'} + + react-is@16.13.1: + resolution: {integrity: sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==} + + react-use-measure@2.1.7: + resolution: {integrity: sha512-KrvcAo13I/60HpwGO5jpW7E9DfusKyLPLvuHlUyP5zqnmAPhNc6qTRjUQrdTADl0lpPpDVU2/Gg51UlOGHXbdg==} + peerDependencies: + react: '>=16.13' + react-dom: '>=16.13' + peerDependenciesMeta: + react-dom: + optional: true + + react@19.2.8: + resolution: {integrity: sha512-PWaYA1L/q9u2u7xYQi+Y3L3Yfnie7XyLeaJICV1MGD6LprsBxcAqGjYyr0eY3p+QdsA+x/Irkt4Qif8D63+Sbw==} + engines: {node: '>=0.10.0'} + + reflect.getprototypeof@1.0.10: + resolution: {integrity: sha512-00o4I+DVrefhv+nX0ulyi3biSHCPDe+yLv5o/p6d/UVlirijB8E16FtfwSAi4g3tcqrQ4lRAqQSoFEZJehYEcw==} + engines: {node: '>= 0.4'} + + regexp.prototype.flags@1.5.4: + resolution: {integrity: sha512-dYqgNSZbDwkaJ2ceRd9ojCGjBq+mOm9LmtXnAnEGyHhN/5R7iDW2TRw3h+o/jCFxus3P2LfWIIiwowAjANm7IA==} + engines: {node: '>= 0.4'} + + resolve-from@4.0.0: + resolution: {integrity: sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==} + engines: {node: '>=4'} + + resolve@2.0.0-next.7: + resolution: {integrity: sha512-tqt+NBWwyaMgw3zDsnygx4CByWjQEJHOPMdslYhppaQSJUtL/D4JO9CcBBlhPoI8lz9oJIDXkwXfhF4aWqP8xQ==} + engines: {node: '>= 0.4'} + hasBin: true + + rolldown@1.2.4: + resolution: {integrity: sha512-rSr7irW0K7QRWzjdJXqZowkcRdDtjRduh43rBltnVKd0VFq839l1lJoDvGJb6gl7+4rTTCrPWu+YfujUL8Ug7w==} + engines: {node: ^20.19.0 || >=22.12.0} + hasBin: true + + safe-array-concat@1.1.4: + resolution: {integrity: sha512-wtZlHyOje6OZTGqAoaDKxFkgRtkF9CnHAVnCHKfuj200wAgL+bSJhdsCD2l0Qx/2ekEXjPWcyKkfGb5CPboslg==} + engines: {node: '>=0.4'} + + safe-push-apply@1.0.0: + resolution: {integrity: sha512-iKE9w/Z7xCzUMIZqdBsp6pEQvwuEebH4vdpjcDWnyzaI6yl6O9FHvVpmGelvEHNsoY6wGblkxR6Zty/h00WiSA==} + engines: {node: '>= 0.4'} + + safe-regex-test@1.1.0: + resolution: {integrity: sha512-x/+Cz4YrimQxQccJf5mKEbIa1NzeCRNI5Ecl/ekmlYaampdNLPalVyIcCZNNH3MvmqBugV5TMYZXv0ljslUlaw==} + engines: {node: '>= 0.4'} + + scheduler@0.27.0: + resolution: {integrity: sha512-eNv+WrVbKu1f3vbYJT/xtiF5syA5HPIMtf9IgY/nKg0sWqzAUEvqY/xm7OcZc/qafLx/iO9FgOmeSAp4v5ti/Q==} + + semver@6.3.1: + resolution: {integrity: sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==} + hasBin: true + + semver@7.8.5: + resolution: {integrity: sha512-Y7/KDsb8LjooZpwaqGyulO6DQlksgCncchHGk+sZIY4SBvUocMBEFH5Ur1fI4dV+Jvl0w6cjvucaIi40puRioA==} + engines: {node: '>=10'} + hasBin: true + + set-function-length@1.2.2: + resolution: {integrity: sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==} + engines: {node: '>= 0.4'} + + set-function-name@2.0.2: + resolution: {integrity: sha512-7PGFlmtwsEADb0WYyvCMa1t+yke6daIG4Wirafur5kcf+MhUnPms1UeR0CKQdTZD81yESwMHbtn+TR+dMviakQ==} + engines: {node: '>= 0.4'} + + set-proto@1.0.0: + resolution: {integrity: sha512-RJRdvCo6IAnPdsvP/7m6bsQqNnn1FCBX5ZNtFL98MmFF/4xAIJTIg1YbHW5DC2W5SKZanrC6i4HsJqlajw/dZw==} + engines: {node: '>= 0.4'} + + shebang-command@2.0.0: + resolution: {integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==} + engines: {node: '>=8'} + + shebang-regex@3.0.0: + resolution: {integrity: sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==} + engines: {node: '>=8'} + + side-channel-list@1.0.1: + resolution: {integrity: sha512-mjn/0bi/oUURjc5Xl7IaWi/OJJJumuoJFQJfDDyO46+hBWsfaVM65TBHq2eoZBhzl9EchxOijpkbRC8SVBQU0w==} + engines: {node: '>= 0.4'} + + side-channel-map@1.0.1: + resolution: {integrity: sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==} + engines: {node: '>= 0.4'} + + side-channel-weakmap@1.0.2: + resolution: {integrity: sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==} + engines: {node: '>= 0.4'} + + side-channel@1.1.1: + resolution: {integrity: sha512-6x6dK6zJdpTzF4sQeNYxwtvBzf6Eg4GtlesS94HOvTudUeyK2WXAaIfmDgsyslYrRBeFIlsi54AYsFGUuhmvrQ==} + engines: {node: '>= 0.4'} + + siginfo@2.0.0: + resolution: {integrity: sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==} + + source-map-js@1.2.1: + resolution: {integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==} + engines: {node: '>=0.10.0'} + + stackback@0.0.2: + resolution: {integrity: sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==} + + std-env@4.2.0: + resolution: {integrity: sha512-oCUKSupKTHX53EyjDtuZQ64pjLJ6yYCtpmEw0goYxtjG9KpbRe8KAsl2tBUGU9DyMcJ0RwJ8GqJAFzMXcXW1Rw==} + + stop-iteration-iterator@1.1.0: + resolution: {integrity: sha512-eLoXW/DHyl62zxY4SCaIgnRhuMr6ri4juEYARS8E6sCEqzKpOiE521Ucofdx+KnDZl5xmvGYaaKCk5FEOxJCoQ==} + engines: {node: '>= 0.4'} + + string.prototype.matchall@4.0.12: + resolution: {integrity: sha512-6CC9uyBL+/48dYizRf7H7VAYCMCNTBeM78x/VTUe9bFEaxBepPJDa1Ow99LqI/1yF7kuy7Q3cQsYMrcjGUcskA==} + engines: {node: '>= 0.4'} + + string.prototype.repeat@1.0.0: + resolution: {integrity: sha512-0u/TldDbKD8bFCQ/4f5+mNRrXwZ8hg2w7ZR8wa16e8z9XpePWl3eGEcUD0OXpEH/VJH/2G3gjUtR3ZOiBe2S/w==} + + string.prototype.trim@1.2.11: + resolution: {integrity: sha512-PwvK7BU+CMTJGYQCTZb5RWXIML92lftJLhQz1tBzgKiqGxJaMlBAa48POXaNAC2s4y8jr3EFqrkF9+44neS46w==} + engines: {node: '>= 0.4'} + + string.prototype.trimend@1.0.10: + resolution: {integrity: sha512-2+3aDAOmPTmuFwjDnmJG2ctEkQKVki7vOSqaxkv42Mowj1V6PnvuwFCRrR5lChUux1TBskPjfkeTOhqczDMxTw==} + engines: {node: '>= 0.4'} + + string.prototype.trimstart@1.0.8: + resolution: {integrity: sha512-UXSH262CSZY1tfu3G3Secr6uGLCFVPMhIqHjlgCUtCCcgihYc/xKs9djMTMUOb2j1mVSeU8EU6NWc/iQKU6Gfg==} + engines: {node: '>= 0.4'} + + strip-bom@3.0.0: + resolution: {integrity: sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==} + engines: {node: '>=4'} + + strip-json-comments@3.1.1: + resolution: {integrity: sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==} + engines: {node: '>=8'} + + supports-color@7.2.0: + resolution: {integrity: sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==} + engines: {node: '>=8'} + + supports-preserve-symlinks-flag@1.0.0: + resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==} + engines: {node: '>= 0.4'} + + suspend-react@0.1.3: + resolution: {integrity: sha512-aqldKgX9aZqpoDp3e8/BZ8Dm7x1pJl+qI3ZKxDN0i/IQTWUwBx/ManmlVJ3wowqbno6c2bmiIfs+Um6LbsjJyQ==} + peerDependencies: + react: '>=17.0' + + synckit@0.11.13: + resolution: {integrity: sha512-eNRKgb3z66Yp3D2CixVujOUvXLFUTij/zVnV8KRyvFdQwpz7I5DS8UfRkTeLzb64u+dkzDSdelE24izu+zSSUg==} + engines: {node: ^14.18.0 || >=16.0.0} + + three@0.182.0: + resolution: {integrity: sha512-GbHabT+Irv+ihI1/f5kIIsZ+Ef9Sl5A1Y7imvS5RQjWgtTPfPnZ43JmlYI7NtCRDK9zir20lQpfg8/9Yd02OvQ==} + + tinybench@2.9.0: + resolution: {integrity: sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg==} + + tinyexec@1.3.0: + resolution: {integrity: sha512-QKAl9m8gWWGHV8jZcPeym6j+XULi6tOf1mT83WYJ4Lk2ytW/uwAWkrP0uFsdoYMdueVJ0qs26wZ+23xeB4ibNQ==} + engines: {node: '>=18'} + + tinyglobby@0.2.17: + resolution: {integrity: sha512-wXR/dYpcqKmfWpEdZjiKJOwCNFndD0DMnrW/cYjVGttEkBfVgcLFHoNrlj47mjOVic9yyNu65alsgF4NQyTa2g==} + engines: {node: '>=12.0.0'} + + tinyrainbow@3.1.1: + resolution: {integrity: sha512-yau8yJdTt989Mm0Bd/236QnzEiPf2xLLTqUZRUJOo/3CB078LSwzei343DgtJVmfJKJE3TMINY1u42SQsP6mXw==} + engines: {node: '>=14.0.0'} + + ts-api-utils@2.5.0: + resolution: {integrity: sha512-OJ/ibxhPlqrMM0UiNHJ/0CKQkoKF243/AEmplt3qpRgkW8VG7IfOS41h7V8TjITqdByHzrjcS/2si+y4lIh8NA==} + engines: {node: '>=18.12'} + peerDependencies: + typescript: '>=4.8.4' + + tsconfig-paths@3.15.0: + resolution: {integrity: sha512-2Ac2RgzDe/cn48GvOe3M+o82pEFewD3UPbyoUHHdKasHwJKjds4fLXWf/Ux5kATBKN20oaFGu+jbElp1pos0mg==} + + type-check@0.4.0: + resolution: {integrity: sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==} + engines: {node: '>= 0.8.0'} + + typed-array-buffer@1.0.3: + resolution: {integrity: sha512-nAYYwfY3qnzX30IkA6AQZjVbtK6duGontcQm1WSG1MD94YLqK0515GNApXkoxKOWMusVssAHWLh9SeaoefYFGw==} + engines: {node: '>= 0.4'} + + typed-array-byte-length@1.0.3: + resolution: {integrity: sha512-BaXgOuIxz8n8pIq3e7Atg/7s+DpiYrxn4vdot3w9KbnBhcRQq6o3xemQdIfynqSeXeDrF32x+WvfzmOjPiY9lg==} + engines: {node: '>= 0.4'} + + typed-array-byte-offset@1.0.4: + resolution: {integrity: sha512-bTlAFB/FBYMcuX81gbL4OcpH5PmlFHqlCCpAl8AlEzMz5k53oNDvN8p1PNOWLEmI2x4orp3raOFB51tv9X+MFQ==} + engines: {node: '>= 0.4'} + + typed-array-length@1.0.8: + resolution: {integrity: sha512-phPGCwqr2+Qo0fwniCE8e4pKnGu/yFb5nD5Y8bf0EEeiI5GklnACYA9GFy/DrAeRrKHXvHn+1SUsOWgJp6RO+g==} + engines: {node: '>= 0.4'} + + typescript@6.0.3: + resolution: {integrity: sha512-y2TvuxSZPDyQakkFRPZHKFm+KKVqIisdg9/CZwm9ftvKXLP8NRWj38/ODjNbr43SsoXqNuAisEf1GdCxqWcdBw==} + engines: {node: '>=14.17'} + hasBin: true + + unbox-primitive@1.1.0: + resolution: {integrity: sha512-nWJ91DjeOkej/TA8pXQ3myruKpKEYgqvpw9lz4OPHj/NWFNluYrjbz9j01CJ8yKQd2g4jFoOkINCTW2I5LEEyw==} + engines: {node: '>= 0.4'} + + undici-types@8.3.0: + resolution: {integrity: sha512-j375ScV60dom+YkPFIfTLcOiPxkN/buHz5GobjLhixFuANaNs3C9l4GmrWqejgXWJ7BbJcFYpTEUkS1Ge8bpZQ==} + + update-browserslist-db@1.3.1: + resolution: {integrity: sha512-ZZ61DsRsOnakl74HAmp3oSN4aXUmEWXf+i/yv0h7tIBfICc3VdrFErQKUUKPgu3AMsTUMbcongALEN4l6GSUrQ==} + hasBin: true + peerDependencies: + browserslist: '>= 4.21.0' + + uri-js@4.4.1: + resolution: {integrity: sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==} + + use-sync-external-store@1.6.0: + resolution: {integrity: sha512-Pp6GSwGP/NrPIrxVFAIkOQeyw8lFenOHijQWkUTrDvrF4ALqylP2C/KCkeS9dpUM3KvYRQhna5vt7IL95+ZQ9w==} + peerDependencies: + react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 + + vite@8.2.1: + resolution: {integrity: sha512-EU/eS7BH3XROHh2YnBefjM6DBKA6ZeMZEYQbj7NLWg5wHYlhB8B/Mayd5XsgWq+NFYccDOTemRpdETWR6Ka/lw==} + engines: {node: ^20.19.0 || >=22.12.0} + hasBin: true + peerDependencies: + '@types/node': ^20.19.0 || >=22.12.0 + '@vitejs/devtools': ^0.4.0 + esbuild: ^0.27.0 || ^0.28.0 + jiti: '>=1.21.0' + less: ^4.0.0 + sass: ^1.70.0 + sass-embedded: ^1.70.0 + stylus: '>=0.54.8' + sugarss: ^5.0.0 + terser: ^5.16.0 + tsx: ^4.8.1 + yaml: ^2.4.2 + peerDependenciesMeta: + '@types/node': + optional: true + '@vitejs/devtools': + optional: true + esbuild: + optional: true + jiti: + optional: true + less: + optional: true + sass: + optional: true + sass-embedded: + optional: true + stylus: + optional: true + sugarss: + optional: true + terser: + optional: true + tsx: + optional: true + yaml: + optional: true + + vitest@4.1.10: + resolution: {integrity: sha512-R9jUTe5S4Qb0HCd4TNqpC7oGcrMssMRGXLW80ubjWsW9VH5GF8y1Y0SFLY9AbqSk6nt0PnOx4H4WNJYZ13GUPw==} + engines: {node: ^20.0.0 || ^22.0.0 || >=24.0.0} + hasBin: true + peerDependencies: + '@edge-runtime/vm': '*' + '@opentelemetry/api': ^1.9.0 + '@types/node': ^20.0.0 || ^22.0.0 || >=24.0.0 + '@vitest/browser-playwright': 4.1.10 + '@vitest/browser-preview': 4.1.10 + '@vitest/browser-webdriverio': 4.1.10 + '@vitest/coverage-istanbul': 4.1.10 + '@vitest/coverage-v8': 4.1.10 + '@vitest/ui': 4.1.10 + happy-dom: '*' + jsdom: '*' + vite: ^6.0.0 || ^7.0.0 || ^8.0.0 + peerDependenciesMeta: + '@edge-runtime/vm': + optional: true + '@opentelemetry/api': + optional: true + '@types/node': + optional: true + '@vitest/browser-playwright': + optional: true + '@vitest/browser-preview': + optional: true + '@vitest/browser-webdriverio': + optional: true + '@vitest/coverage-istanbul': + optional: true + '@vitest/coverage-v8': + optional: true + '@vitest/ui': + optional: true + happy-dom: + optional: true + jsdom: + optional: true + + which-boxed-primitive@1.1.1: + resolution: {integrity: sha512-TbX3mj8n0odCBFVlY8AxkqcHASw3L60jIuF8jFP78az3C2YhmGvqbHBpAjTRH2/xqYunrJ9g1jSyjCjpoWzIAA==} + engines: {node: '>= 0.4'} + + which-builtin-type@1.2.1: + resolution: {integrity: sha512-6iBczoX+kDQ7a3+YJBnh3T+KZRxM/iYNPXicqk66/Qfm1b93iu+yOImkg0zHbj5LNOcNv1TEADiZ0xa34B4q6Q==} + engines: {node: '>= 0.4'} + + which-collection@1.0.2: + resolution: {integrity: sha512-K4jVyjnBdgvc86Y6BkaLZEN933SwYOuBFkdmBu9ZfkcAbdVbpITnDmjvZ/aQjRXQrv5EPkTnD1s39GiiqbngCw==} + engines: {node: '>= 0.4'} + + which-typed-array@1.1.22: + resolution: {integrity: sha512-fvO4ExWMFsqyhG3AiPAObMuY1lxaqgYcxbc49CNdWDDECOJNgQyvsOWVwbZc+qf3rzRtxojBK+CMEv0Ld5CYpw==} + engines: {node: '>= 0.4'} + + which@2.0.2: + resolution: {integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==} + engines: {node: '>= 8'} + hasBin: true + + why-is-node-running@2.3.0: + resolution: {integrity: sha512-hUrmaWBdVDcxvYqnyh09zunKzROWjbZTiNy8dBEjkS7ehEDQibXJ7XvlmtbwuTclUiIyN+CyXQD4Vmko8fNm8w==} + engines: {node: '>=8'} + hasBin: true + + word-wrap@1.2.5: + resolution: {integrity: sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==} + engines: {node: '>=0.10.0'} + + yallist@3.1.1: + resolution: {integrity: sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==} + + yocto-queue@0.1.0: + resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==} + engines: {node: '>=10'} + + zod-validation-error@4.0.2: + resolution: {integrity: sha512-Q6/nZLe6jxuU80qb/4uJ4t5v2VEZ44lzQjPDhYJNztRQ4wyWc6VF3D3Kb/fAuPetZQnhS3hnajCf9CsWesghLQ==} + engines: {node: '>=18.0.0'} + peerDependencies: + zod: ^3.25.0 || ^4.0.0 + + zod@4.4.3: + resolution: {integrity: sha512-ytENFjIJFl2UwYglde2jchW2Hwm4GJFLDiSXWdTrJQBIN9Fcyp7n4DhxJEiWNAJMV1/BqWfW/kkg71UDcHJyTQ==} + + zustand@5.0.15: + resolution: {integrity: sha512-MpSEjRiBkA9crSYeOUH32rJC7SVqAbm0Fqcqge/bUi2PPoLcBWKOsG+C8mevmpr8TwXHBVkChbbJiyvkE+i/3A==} + engines: {node: '>=12.20.0'} + peerDependencies: + '@types/react': '>=18.0.0' + immer: '>=9.0.6' + react: '>=18.0.0' + use-sync-external-store: '>=1.2.0' + peerDependenciesMeta: + '@types/react': + optional: true + immer: + optional: true + react: + optional: true + use-sync-external-store: + optional: true + +snapshots: + + '@babel/code-frame@7.29.7': + dependencies: + '@babel/helper-validator-identifier': 7.29.7 + js-tokens: 4.0.0 + picocolors: 1.1.1 + + '@babel/compat-data@7.29.7': {} + + '@babel/core@7.29.7(supports-color@7.2.0)': + dependencies: + '@babel/code-frame': 7.29.7 + '@babel/generator': 7.29.8 + '@babel/helper-compilation-targets': 7.29.7 + '@babel/helper-module-transforms': 7.29.7(@babel/core@7.29.7(supports-color@7.2.0))(supports-color@7.2.0) + '@babel/helpers': 7.29.7 + '@babel/parser': 7.29.8 + '@babel/template': 7.29.7 + '@babel/traverse': 7.29.8(supports-color@7.2.0) + '@babel/types': 7.29.8 + '@jridgewell/remapping': 2.3.5 + convert-source-map: 2.0.0 + debug: 4.4.3(supports-color@7.2.0) + gensync: 1.0.0-beta.2 + json5: 2.2.3 + semver: 6.3.1 + transitivePeerDependencies: + - supports-color + + '@babel/generator@7.29.8': + dependencies: + '@babel/parser': 7.29.8 + '@babel/types': 7.29.8 + '@jridgewell/gen-mapping': 0.3.13 + '@jridgewell/trace-mapping': 0.3.31 + jsesc: 3.1.0 + + '@babel/helper-compilation-targets@7.29.7': + dependencies: + '@babel/compat-data': 7.29.7 + '@babel/helper-validator-option': 7.29.7 + browserslist: 4.28.8 + lru-cache: 5.1.1 + semver: 6.3.1 + + '@babel/helper-globals@7.29.7': {} + + '@babel/helper-module-imports@7.29.7(supports-color@7.2.0)': + dependencies: + '@babel/traverse': 7.29.8(supports-color@7.2.0) + '@babel/types': 7.29.8 + transitivePeerDependencies: + - supports-color + + '@babel/helper-module-transforms@7.29.7(@babel/core@7.29.7(supports-color@7.2.0))(supports-color@7.2.0)': + dependencies: + '@babel/core': 7.29.7(supports-color@7.2.0) + '@babel/helper-module-imports': 7.29.7(supports-color@7.2.0) + '@babel/helper-validator-identifier': 7.29.7 + '@babel/traverse': 7.29.8(supports-color@7.2.0) + transitivePeerDependencies: + - supports-color + + '@babel/helper-string-parser@7.29.7': {} + + '@babel/helper-validator-identifier@7.29.7': {} + + '@babel/helper-validator-option@7.29.7': {} + + '@babel/helpers@7.29.7': + dependencies: + '@babel/template': 7.29.7 + '@babel/types': 7.29.8 + + '@babel/parser@7.29.8': + dependencies: + '@babel/types': 7.29.8 + + '@babel/runtime@7.29.7': {} + + '@babel/template@7.29.7': + dependencies: + '@babel/code-frame': 7.29.7 + '@babel/parser': 7.29.8 + '@babel/types': 7.29.8 + + '@babel/traverse@7.29.8(supports-color@7.2.0)': + dependencies: + '@babel/code-frame': 7.29.7 + '@babel/generator': 7.29.8 + '@babel/helper-globals': 7.29.7 + '@babel/parser': 7.29.8 + '@babel/template': 7.29.7 + '@babel/types': 7.29.8 + debug: 4.4.3(supports-color@7.2.0) + transitivePeerDependencies: + - supports-color + + '@babel/types@7.29.8': + dependencies: + '@babel/helper-string-parser': 7.29.7 + '@babel/helper-validator-identifier': 7.29.7 + + '@dimforge/rapier3d-compat@0.12.0': {} + + '@eslint-community/eslint-utils@4.10.1(eslint@9.39.5(supports-color@7.2.0))': + dependencies: + eslint: 9.39.5(supports-color@7.2.0) + eslint-visitor-keys: 3.4.3 + + '@eslint-community/regexpp@4.12.2': {} + + '@eslint/compat@2.1.0(eslint@9.39.5(supports-color@7.2.0))': + dependencies: + '@eslint/core': 1.2.1 + optionalDependencies: + eslint: 9.39.5(supports-color@7.2.0) + + '@eslint/config-array@0.21.2(supports-color@7.2.0)': + dependencies: + '@eslint/object-schema': 2.1.7 + debug: 4.4.3(supports-color@7.2.0) + minimatch: 3.1.5 + transitivePeerDependencies: + - supports-color + + '@eslint/config-helpers@0.4.2': + dependencies: + '@eslint/core': 0.17.0 + + '@eslint/core@0.17.0': + dependencies: + '@types/json-schema': 7.0.15 + + '@eslint/core@1.2.1': + dependencies: + '@types/json-schema': 7.0.15 + + '@eslint/eslintrc@3.3.6(supports-color@7.2.0)': + dependencies: + ajv: 6.15.0 + debug: 4.4.3(supports-color@7.2.0) + espree: 10.4.0 + globals: 14.0.0 + ignore: 5.3.2 + import-fresh: 3.3.1 + js-yaml: 4.3.1 + minimatch: 3.1.5 + strip-json-comments: 3.1.1 + transitivePeerDependencies: + - supports-color + + '@eslint/js@9.39.5': {} + + '@eslint/object-schema@2.1.7': {} + + '@eslint/plugin-kit@0.4.1': + dependencies: + '@eslint/core': 0.17.0 + levn: 0.4.1 + + '@humanfs/core@0.19.2': + dependencies: + '@humanfs/types': 0.15.0 + + '@humanfs/node@0.16.8': + dependencies: + '@humanfs/core': 0.19.2 + '@humanfs/types': 0.15.0 + '@humanwhocodes/retry': 0.4.3 + + '@humanfs/types@0.15.0': {} + + '@humanwhocodes/module-importer@1.0.1': {} + + '@humanwhocodes/retry@0.4.3': {} + + '@jridgewell/gen-mapping@0.3.13': + dependencies: + '@jridgewell/sourcemap-codec': 1.5.5 + '@jridgewell/trace-mapping': 0.3.31 + + '@jridgewell/remapping@2.3.5': + dependencies: + '@jridgewell/gen-mapping': 0.3.13 + '@jridgewell/trace-mapping': 0.3.31 + + '@jridgewell/resolve-uri@3.1.2': {} + + '@jridgewell/sourcemap-codec@1.5.5': {} + + '@jridgewell/trace-mapping@0.3.31': + dependencies: + '@jridgewell/resolve-uri': 3.1.2 + '@jridgewell/sourcemap-codec': 1.5.5 + + '@oxc-project/types@0.144.0': {} + + '@pkgr/core@0.3.6': {} + + '@react-three/fiber@9.7.0(@types/react@19.2.18)(react@19.2.8)(three@0.182.0)': + dependencies: + '@babel/runtime': 7.29.7 + '@types/webxr': 0.5.24 + base64-js: 1.5.1 + buffer: 6.0.3 + its-fine: 2.0.0(@types/react@19.2.18)(react@19.2.8) + react: 19.2.8 + react-use-measure: 2.1.7(react@19.2.8) + scheduler: 0.27.0 + suspend-react: 0.1.3(react@19.2.8) + three: 0.182.0 + use-sync-external-store: 1.6.0(react@19.2.8) + zustand: 5.0.15(@types/react@19.2.18)(react@19.2.8)(use-sync-external-store@1.6.0(react@19.2.8)) + transitivePeerDependencies: + - '@types/react' + - immer + + '@rolldown/binding-android-arm64@1.2.4': + optional: true + + '@rolldown/binding-darwin-arm64@1.2.4': + optional: true + + '@rolldown/binding-darwin-x64@1.2.4': + optional: true + + '@rolldown/binding-freebsd-x64@1.2.4': + optional: true + + '@rolldown/binding-linux-arm-gnueabihf@1.2.4': + optional: true + + '@rolldown/binding-linux-arm64-gnu@1.2.4': + optional: true + + '@rolldown/binding-linux-arm64-musl@1.2.4': + optional: true + + '@rolldown/binding-linux-ppc64-gnu@1.2.4': + optional: true + + '@rolldown/binding-linux-s390x-gnu@1.2.4': + optional: true + + '@rolldown/binding-linux-x64-gnu@1.2.4': + optional: true + + '@rolldown/binding-linux-x64-musl@1.2.4': + optional: true + + '@rolldown/binding-openharmony-arm64@1.2.4': + optional: true + + '@rolldown/binding-win32-arm64-msvc@1.2.4': + optional: true + + '@rolldown/binding-win32-x64-msvc@1.2.4': + optional: true + + '@rolldown/pluginutils@1.0.1': {} + + '@rtsao/scc@1.1.0': {} + + '@standard-schema/spec@1.1.0': {} + + '@tweenjs/tween.js@23.1.3': {} + + '@types/chai@5.2.3': + dependencies: + '@types/deep-eql': 4.0.2 + assertion-error: 2.0.1 + + '@types/deep-eql@4.0.2': {} + + '@types/estree@1.0.9': {} + + '@types/json-schema@7.0.15': {} + + '@types/json5@0.0.29': {} + + '@types/node@26.2.0': + dependencies: + undici-types: 8.3.0 + + '@types/react-reconciler@0.28.9(@types/react@19.2.18)': + dependencies: + '@types/react': 19.2.18 + + '@types/react@19.2.18': + dependencies: + csstype: 3.2.3 + + '@types/stats.js@0.17.4': {} + + '@types/three@0.182.0': + dependencies: + '@dimforge/rapier3d-compat': 0.12.0 + '@tweenjs/tween.js': 23.1.3 + '@types/stats.js': 0.17.4 + '@types/webxr': 0.5.24 + '@webgpu/types': 0.1.71 + fflate: 0.8.3 + meshoptimizer: 0.22.0 + + '@types/webxr@0.5.24': {} + + '@typescript-eslint/eslint-plugin@8.67.0(@typescript-eslint/parser@8.67.0(eslint@9.39.5(supports-color@7.2.0))(supports-color@7.2.0)(typescript@6.0.3))(eslint@9.39.5(supports-color@7.2.0))(supports-color@7.2.0)(typescript@6.0.3)': + dependencies: + '@eslint-community/regexpp': 4.12.2 + '@typescript-eslint/parser': 8.67.0(eslint@9.39.5(supports-color@7.2.0))(supports-color@7.2.0)(typescript@6.0.3) + '@typescript-eslint/scope-manager': 8.67.0 + '@typescript-eslint/type-utils': 8.67.0(eslint@9.39.5(supports-color@7.2.0))(supports-color@7.2.0)(typescript@6.0.3) + '@typescript-eslint/utils': 8.67.0(eslint@9.39.5(supports-color@7.2.0))(supports-color@7.2.0)(typescript@6.0.3) + '@typescript-eslint/visitor-keys': 8.67.0 + eslint: 9.39.5(supports-color@7.2.0) + ignore: 7.0.6 + natural-compare: 1.4.0 + ts-api-utils: 2.5.0(typescript@6.0.3) + typescript: 6.0.3 + transitivePeerDependencies: + - supports-color + + '@typescript-eslint/parser@8.67.0(eslint@9.39.5(supports-color@7.2.0))(supports-color@7.2.0)(typescript@6.0.3)': + dependencies: + '@typescript-eslint/scope-manager': 8.67.0 + '@typescript-eslint/types': 8.67.0 + '@typescript-eslint/typescript-estree': 8.67.0(supports-color@7.2.0)(typescript@6.0.3) + '@typescript-eslint/visitor-keys': 8.67.0 + debug: 4.4.3(supports-color@7.2.0) + eslint: 9.39.5(supports-color@7.2.0) + typescript: 6.0.3 + transitivePeerDependencies: + - supports-color + + '@typescript-eslint/project-service@8.67.0(supports-color@7.2.0)(typescript@6.0.3)': + dependencies: + '@typescript-eslint/tsconfig-utils': 8.67.0(typescript@6.0.3) + '@typescript-eslint/types': 8.67.0 + debug: 4.4.3(supports-color@7.2.0) + typescript: 6.0.3 + transitivePeerDependencies: + - supports-color + + '@typescript-eslint/scope-manager@8.67.0': + dependencies: + '@typescript-eslint/types': 8.67.0 + '@typescript-eslint/visitor-keys': 8.67.0 + + '@typescript-eslint/tsconfig-utils@8.67.0(typescript@6.0.3)': + dependencies: + typescript: 6.0.3 + + '@typescript-eslint/type-utils@8.67.0(eslint@9.39.5(supports-color@7.2.0))(supports-color@7.2.0)(typescript@6.0.3)': + dependencies: + '@typescript-eslint/types': 8.67.0 + '@typescript-eslint/typescript-estree': 8.67.0(supports-color@7.2.0)(typescript@6.0.3) + '@typescript-eslint/utils': 8.67.0(eslint@9.39.5(supports-color@7.2.0))(supports-color@7.2.0)(typescript@6.0.3) + debug: 4.4.3(supports-color@7.2.0) + eslint: 9.39.5(supports-color@7.2.0) + ts-api-utils: 2.5.0(typescript@6.0.3) + typescript: 6.0.3 + transitivePeerDependencies: + - supports-color + + '@typescript-eslint/types@8.67.0': {} + + '@typescript-eslint/typescript-estree@8.67.0(supports-color@7.2.0)(typescript@6.0.3)': + dependencies: + '@typescript-eslint/project-service': 8.67.0(supports-color@7.2.0)(typescript@6.0.3) + '@typescript-eslint/tsconfig-utils': 8.67.0(typescript@6.0.3) + '@typescript-eslint/types': 8.67.0 + '@typescript-eslint/visitor-keys': 8.67.0 + debug: 4.4.3(supports-color@7.2.0) + minimatch: 10.2.6 + semver: 7.8.5 + tinyglobby: 0.2.17 + ts-api-utils: 2.5.0(typescript@6.0.3) + typescript: 6.0.3 + transitivePeerDependencies: + - supports-color + + '@typescript-eslint/utils@8.67.0(eslint@9.39.5(supports-color@7.2.0))(supports-color@7.2.0)(typescript@6.0.3)': + dependencies: + '@eslint-community/eslint-utils': 4.10.1(eslint@9.39.5(supports-color@7.2.0)) + '@typescript-eslint/scope-manager': 8.67.0 + '@typescript-eslint/types': 8.67.0 + '@typescript-eslint/typescript-estree': 8.67.0(supports-color@7.2.0)(typescript@6.0.3) + eslint: 9.39.5(supports-color@7.2.0) + typescript: 6.0.3 + transitivePeerDependencies: + - supports-color + + '@typescript-eslint/visitor-keys@8.67.0': + dependencies: + '@typescript-eslint/types': 8.67.0 + eslint-visitor-keys: 5.0.1 + + '@vitest/expect@4.1.10': + dependencies: + '@standard-schema/spec': 1.1.0 + '@types/chai': 5.2.3 + '@vitest/spy': 4.1.10 + '@vitest/utils': 4.1.10 + chai: 6.2.2 + tinyrainbow: 3.1.1 + + '@vitest/mocker@4.1.10(vite@8.2.1(@types/node@26.2.0))': + dependencies: + '@vitest/spy': 4.1.10 + estree-walker: 3.0.3 + magic-string: 0.30.21 + optionalDependencies: + vite: 8.2.1(@types/node@26.2.0) + + '@vitest/pretty-format@4.1.10': + dependencies: + tinyrainbow: 3.1.1 + + '@vitest/runner@4.1.10': + dependencies: + '@vitest/utils': 4.1.10 + pathe: 2.0.3 + + '@vitest/snapshot@4.1.10': + dependencies: + '@vitest/pretty-format': 4.1.10 + '@vitest/utils': 4.1.10 + magic-string: 0.30.21 + pathe: 2.0.3 + + '@vitest/spy@4.1.10': {} + + '@vitest/utils@4.1.10': + dependencies: + '@vitest/pretty-format': 4.1.10 + convert-source-map: 2.0.0 + tinyrainbow: 3.1.1 + + '@webgpu/types@0.1.71': {} + + acorn-jsx@5.3.2(acorn@8.18.0): + dependencies: + acorn: 8.18.0 + + acorn@8.18.0: {} + + ajv@6.15.0: + dependencies: + fast-deep-equal: 3.1.3 + fast-json-stable-stringify: 2.1.0 + json-schema-traverse: 0.4.1 + uri-js: 4.4.1 + + ansi-styles@4.3.0: + dependencies: + color-convert: 2.0.1 + + argparse@2.0.1: {} + + array-buffer-byte-length@1.0.2: + dependencies: + call-bound: 1.0.4 + is-array-buffer: 3.0.5 + + array-includes@3.1.9: + dependencies: + call-bind: 1.0.9 + call-bound: 1.0.4 + define-properties: 1.2.1 + es-abstract: 1.24.2 + es-object-atoms: 1.1.2 + get-intrinsic: 1.3.0 + is-string: 1.1.1 + math-intrinsics: 1.1.0 + + array.prototype.findlast@1.2.5: + dependencies: + call-bind: 1.0.9 + define-properties: 1.2.1 + es-abstract: 1.24.2 + es-errors: 1.3.0 + es-object-atoms: 1.1.2 + es-shim-unscopables: 1.1.0 + + array.prototype.findlastindex@1.2.6: + dependencies: + call-bind: 1.0.9 + call-bound: 1.0.4 + define-properties: 1.2.1 + es-abstract: 1.24.2 + es-errors: 1.3.0 + es-object-atoms: 1.1.2 + es-shim-unscopables: 1.1.0 + + array.prototype.flat@1.3.3: + dependencies: + call-bind: 1.0.9 + define-properties: 1.2.1 + es-abstract: 1.24.2 + es-shim-unscopables: 1.1.0 + + array.prototype.flatmap@1.3.3: + dependencies: + call-bind: 1.0.9 + define-properties: 1.2.1 + es-abstract: 1.24.2 + es-shim-unscopables: 1.1.0 + + array.prototype.tosorted@1.1.4: + dependencies: + call-bind: 1.0.9 + define-properties: 1.2.1 + es-abstract: 1.24.2 + es-errors: 1.3.0 + es-shim-unscopables: 1.1.0 + + arraybuffer.prototype.slice@1.0.4: + dependencies: + array-buffer-byte-length: 1.0.2 + call-bind: 1.0.9 + define-properties: 1.2.1 + es-abstract: 1.24.2 + es-errors: 1.3.0 + get-intrinsic: 1.3.0 + is-array-buffer: 3.0.5 + + assertion-error@2.0.1: {} + + async-function@1.0.0: {} + + available-typed-arrays@1.0.7: + dependencies: + possible-typed-array-names: 1.1.0 + + balanced-match@1.0.2: {} + + balanced-match@4.0.4: {} + + base64-js@1.5.1: {} + + baseline-browser-mapping@2.11.14: {} + + brace-expansion@1.1.18: + dependencies: + balanced-match: 1.0.2 + concat-map: 0.0.1 + + brace-expansion@5.0.9: + dependencies: + balanced-match: 4.0.4 + + browserslist@4.28.8: + dependencies: + baseline-browser-mapping: 2.11.14 + caniuse-lite: 1.0.30001809 + electron-to-chromium: 1.5.406 + node-releases: 2.0.53 + update-browserslist-db: 1.3.1(browserslist@4.28.8) + + buffer@6.0.3: + dependencies: + base64-js: 1.5.1 + ieee754: 1.2.1 + + call-bind-apply-helpers@1.0.2: + dependencies: + es-errors: 1.3.0 + function-bind: 1.1.2 + + call-bind@1.0.9: + dependencies: + call-bind-apply-helpers: 1.0.2 + es-define-property: 1.0.1 + get-intrinsic: 1.3.0 + set-function-length: 1.2.2 + + call-bound@1.0.4: + dependencies: + call-bind-apply-helpers: 1.0.2 + get-intrinsic: 1.3.0 + + callsites@3.1.0: {} + + caniuse-lite@1.0.30001809: {} + + chai@6.2.2: {} + + chalk@4.1.2: + dependencies: + ansi-styles: 4.3.0 + supports-color: 7.2.0 + + color-convert@2.0.1: + dependencies: + color-name: 1.1.4 + + color-name@1.1.4: {} + + concat-map@0.0.1: {} + + convert-source-map@2.0.0: {} + + cross-spawn@7.0.6: + dependencies: + path-key: 3.1.1 + shebang-command: 2.0.0 + which: 2.0.2 + + csstype@3.2.3: {} + + data-view-buffer@1.0.2: + dependencies: + call-bound: 1.0.4 + es-errors: 1.3.0 + is-data-view: 1.0.2 + + data-view-byte-length@1.0.2: + dependencies: + call-bound: 1.0.4 + es-errors: 1.3.0 + is-data-view: 1.0.2 + + data-view-byte-offset@1.0.1: + dependencies: + call-bound: 1.0.4 + es-errors: 1.3.0 + is-data-view: 1.0.2 + + debug@3.2.7(supports-color@7.2.0): + dependencies: + ms: 2.1.3 + optionalDependencies: + supports-color: 7.2.0 + + debug@4.4.3(supports-color@7.2.0): + dependencies: + ms: 2.1.3 + optionalDependencies: + supports-color: 7.2.0 + + deep-is@0.1.4: {} + + define-data-property@1.1.4: + dependencies: + es-define-property: 1.0.1 + es-errors: 1.3.0 + gopd: 1.2.0 + + define-properties@1.2.1: + dependencies: + define-data-property: 1.1.4 + has-property-descriptors: 1.0.2 + object-keys: 1.1.1 + + detect-libc@2.1.2: {} + + doctrine@2.1.0: + dependencies: + esutils: 2.0.3 + + dunder-proto@1.0.1: + dependencies: + call-bind-apply-helpers: 1.0.2 + es-errors: 1.3.0 + gopd: 1.2.0 + + electron-to-chromium@1.5.406: {} + + es-abstract-get@1.0.0: + dependencies: + es-errors: 1.3.0 + es-object-atoms: 1.1.2 + is-callable: 1.2.7 + object-inspect: 1.13.4 + + es-abstract@1.24.2: + dependencies: + array-buffer-byte-length: 1.0.2 + arraybuffer.prototype.slice: 1.0.4 + available-typed-arrays: 1.0.7 + call-bind: 1.0.9 + call-bound: 1.0.4 + data-view-buffer: 1.0.2 + data-view-byte-length: 1.0.2 + data-view-byte-offset: 1.0.1 + es-define-property: 1.0.1 + es-errors: 1.3.0 + es-object-atoms: 1.1.2 + es-set-tostringtag: 2.1.0 + es-to-primitive: 1.3.4 + function.prototype.name: 1.2.0 + get-intrinsic: 1.3.0 + get-proto: 1.0.1 + get-symbol-description: 1.1.0 + globalthis: 1.0.4 + gopd: 1.2.0 + has-property-descriptors: 1.0.2 + has-proto: 1.2.0 + has-symbols: 1.1.0 + hasown: 2.0.4 + internal-slot: 1.1.0 + is-array-buffer: 3.0.5 + is-callable: 1.2.7 + is-data-view: 1.0.2 + is-negative-zero: 2.0.3 + is-regex: 1.2.1 + is-set: 2.0.3 + is-shared-array-buffer: 1.0.4 + is-string: 1.1.1 + is-typed-array: 1.1.15 + is-weakref: 1.1.1 + math-intrinsics: 1.1.0 + object-inspect: 1.13.4 + object-keys: 1.1.1 + object.assign: 4.1.7 + own-keys: 1.0.2 + regexp.prototype.flags: 1.5.4 + safe-array-concat: 1.1.4 + safe-push-apply: 1.0.0 + safe-regex-test: 1.1.0 + set-proto: 1.0.0 + stop-iteration-iterator: 1.1.0 + string.prototype.trim: 1.2.11 + string.prototype.trimend: 1.0.10 + string.prototype.trimstart: 1.0.8 + typed-array-buffer: 1.0.3 + typed-array-byte-length: 1.0.3 + typed-array-byte-offset: 1.0.4 + typed-array-length: 1.0.8 + unbox-primitive: 1.1.0 + which-typed-array: 1.1.22 + + es-define-property@1.0.1: {} + + es-errors@1.3.0: {} + + es-iterator-helpers@1.4.0: + dependencies: + call-bind: 1.0.9 + call-bound: 1.0.4 + define-properties: 1.2.1 + es-abstract: 1.24.2 + es-errors: 1.3.0 + es-set-tostringtag: 2.1.0 + function-bind: 1.1.2 + get-intrinsic: 1.3.0 + globalthis: 1.0.4 + gopd: 1.2.0 + has-property-descriptors: 1.0.2 + has-proto: 1.2.0 + has-symbols: 1.1.0 + internal-slot: 1.1.0 + iterator.prototype: 1.1.5 + math-intrinsics: 1.1.0 + + es-module-lexer@2.3.1: {} + + es-object-atoms@1.1.2: + dependencies: + es-errors: 1.3.0 + + es-set-tostringtag@2.1.0: + dependencies: + es-errors: 1.3.0 + get-intrinsic: 1.3.0 + has-tostringtag: 1.0.2 + hasown: 2.0.4 + + es-shim-unscopables@1.1.0: + dependencies: + hasown: 2.0.4 + + es-to-primitive@1.3.4: + dependencies: + es-abstract-get: 1.0.0 + es-define-property: 1.0.1 + es-errors: 1.3.0 + is-callable: 1.2.7 + is-date-object: 1.1.0 + is-symbol: 1.1.1 + + escalade@3.2.0: {} + + escape-string-regexp@4.0.0: {} + + eslint-config-prettier@10.1.8(eslint@9.39.5(supports-color@7.2.0)): + dependencies: + eslint: 9.39.5(supports-color@7.2.0) + + eslint-import-resolver-alias@1.1.2(eslint-plugin-import@2.32.0(@typescript-eslint/parser@8.67.0(eslint@9.39.5(supports-color@7.2.0))(supports-color@7.2.0)(typescript@6.0.3))(eslint@9.39.5(supports-color@7.2.0))(supports-color@7.2.0)): + dependencies: + eslint-plugin-import: 2.32.0(@typescript-eslint/parser@8.67.0(eslint@9.39.5(supports-color@7.2.0))(supports-color@7.2.0)(typescript@6.0.3))(eslint@9.39.5(supports-color@7.2.0))(supports-color@7.2.0) + + eslint-import-resolver-node@0.3.10(supports-color@7.2.0): + dependencies: + debug: 3.2.7(supports-color@7.2.0) + is-core-module: 2.16.2 + resolve: 2.0.0-next.7 + transitivePeerDependencies: + - supports-color + + eslint-module-utils@2.14.0(@typescript-eslint/parser@8.67.0(eslint@9.39.5(supports-color@7.2.0))(supports-color@7.2.0)(typescript@6.0.3))(eslint-import-resolver-node@0.3.10(supports-color@7.2.0))(eslint@9.39.5(supports-color@7.2.0))(supports-color@7.2.0): + dependencies: + debug: 3.2.7(supports-color@7.2.0) + optionalDependencies: + '@typescript-eslint/parser': 8.67.0(eslint@9.39.5(supports-color@7.2.0))(supports-color@7.2.0)(typescript@6.0.3) + eslint: 9.39.5(supports-color@7.2.0) + eslint-import-resolver-node: 0.3.10(supports-color@7.2.0) + transitivePeerDependencies: + - supports-color + + eslint-plugin-import@2.32.0(@typescript-eslint/parser@8.67.0(eslint@9.39.5(supports-color@7.2.0))(supports-color@7.2.0)(typescript@6.0.3))(eslint@9.39.5(supports-color@7.2.0))(supports-color@7.2.0): + dependencies: + '@rtsao/scc': 1.1.0 + array-includes: 3.1.9 + array.prototype.findlastindex: 1.2.6 + array.prototype.flat: 1.3.3 + array.prototype.flatmap: 1.3.3 + debug: 3.2.7(supports-color@7.2.0) + doctrine: 2.1.0 + eslint: 9.39.5(supports-color@7.2.0) + eslint-import-resolver-node: 0.3.10(supports-color@7.2.0) + eslint-module-utils: 2.14.0(@typescript-eslint/parser@8.67.0(eslint@9.39.5(supports-color@7.2.0))(supports-color@7.2.0)(typescript@6.0.3))(eslint-import-resolver-node@0.3.10(supports-color@7.2.0))(eslint@9.39.5(supports-color@7.2.0))(supports-color@7.2.0) + hasown: 2.0.4 + is-core-module: 2.16.2 + is-glob: 4.0.3 + minimatch: 3.1.5 + object.fromentries: 2.0.8 + object.groupby: 1.0.3 + object.values: 1.2.1 + semver: 6.3.1 + string.prototype.trimend: 1.0.10 + tsconfig-paths: 3.15.0 + optionalDependencies: + '@typescript-eslint/parser': 8.67.0(eslint@9.39.5(supports-color@7.2.0))(supports-color@7.2.0)(typescript@6.0.3) + transitivePeerDependencies: + - eslint-import-resolver-typescript + - eslint-import-resolver-webpack + - supports-color + + eslint-plugin-prettier@5.5.6(eslint-config-prettier@10.1.8(eslint@9.39.5(supports-color@7.2.0)))(eslint@9.39.5(supports-color@7.2.0))(prettier@3.9.6): + dependencies: + eslint: 9.39.5(supports-color@7.2.0) + prettier: 3.9.6 + prettier-linter-helpers: 1.0.1 + synckit: 0.11.13 + optionalDependencies: + eslint-config-prettier: 10.1.8(eslint@9.39.5(supports-color@7.2.0)) + + eslint-plugin-react-hooks@7.1.1(eslint@9.39.5(supports-color@7.2.0))(supports-color@7.2.0): + dependencies: + '@babel/core': 7.29.7(supports-color@7.2.0) + '@babel/parser': 7.29.8 + eslint: 9.39.5(supports-color@7.2.0) + hermes-parser: 0.25.1 + zod: 4.4.3 + zod-validation-error: 4.0.2(zod@4.4.3) + transitivePeerDependencies: + - supports-color + + eslint-plugin-react@7.37.5(eslint@9.39.5(supports-color@7.2.0)): + dependencies: + array-includes: 3.1.9 + array.prototype.findlast: 1.2.5 + array.prototype.flatmap: 1.3.3 + array.prototype.tosorted: 1.1.4 + doctrine: 2.1.0 + es-iterator-helpers: 1.4.0 + eslint: 9.39.5(supports-color@7.2.0) + estraverse: 5.3.0 + hasown: 2.0.4 + jsx-ast-utils: 3.3.5 + minimatch: 3.1.5 + object.entries: 1.1.9 + object.fromentries: 2.0.8 + object.values: 1.2.1 + prop-types: 15.8.1 + resolve: 2.0.0-next.7 + semver: 6.3.1 + string.prototype.matchall: 4.0.12 + string.prototype.repeat: 1.0.0 + + eslint-scope@8.4.0: + dependencies: + esrecurse: 4.3.0 + estraverse: 5.3.0 + + eslint-visitor-keys@3.4.3: {} + + eslint-visitor-keys@4.2.1: {} + + eslint-visitor-keys@5.0.1: {} + + eslint@9.39.5(supports-color@7.2.0): + dependencies: + '@eslint-community/eslint-utils': 4.10.1(eslint@9.39.5(supports-color@7.2.0)) + '@eslint-community/regexpp': 4.12.2 + '@eslint/config-array': 0.21.2(supports-color@7.2.0) + '@eslint/config-helpers': 0.4.2 + '@eslint/core': 0.17.0 + '@eslint/eslintrc': 3.3.6(supports-color@7.2.0) + '@eslint/js': 9.39.5 + '@eslint/plugin-kit': 0.4.1 + '@humanfs/node': 0.16.8 + '@humanwhocodes/module-importer': 1.0.1 + '@humanwhocodes/retry': 0.4.3 + '@types/estree': 1.0.9 + ajv: 6.15.0 + chalk: 4.1.2 + cross-spawn: 7.0.6 + debug: 4.4.3(supports-color@7.2.0) + escape-string-regexp: 4.0.0 + eslint-scope: 8.4.0 + eslint-visitor-keys: 4.2.1 + espree: 10.4.0 + esquery: 1.7.0 + esutils: 2.0.3 + fast-deep-equal: 3.1.3 + file-entry-cache: 8.0.0 + find-up: 5.0.0 + glob-parent: 6.0.2 + ignore: 5.3.2 + imurmurhash: 0.1.4 + is-glob: 4.0.3 + json-stable-stringify-without-jsonify: 1.0.1 + lodash.merge: 4.6.2 + minimatch: 3.1.5 + natural-compare: 1.4.0 + optionator: 0.9.4 + transitivePeerDependencies: + - supports-color + + espree@10.4.0: + dependencies: + acorn: 8.18.0 + acorn-jsx: 5.3.2(acorn@8.18.0) + eslint-visitor-keys: 4.2.1 + + esquery@1.7.0: + dependencies: + estraverse: 5.3.0 + + esrecurse@4.3.0: + dependencies: + estraverse: 5.3.0 + + estraverse@5.3.0: {} + + estree-walker@3.0.3: + dependencies: + '@types/estree': 1.0.9 + + esutils@2.0.3: {} + + expect-type@1.4.0: {} + + fast-deep-equal@3.1.3: {} + + fast-diff@1.3.0: {} + + fast-json-stable-stringify@2.1.0: {} + + fast-levenshtein@2.0.6: {} + + fdir@6.5.0(picomatch@4.0.5): + optionalDependencies: + picomatch: 4.0.5 + + fflate@0.8.3: {} + + file-entry-cache@8.0.0: + dependencies: + flat-cache: 4.0.1 + + find-up@5.0.0: + dependencies: + locate-path: 6.0.0 + path-exists: 4.0.0 + + flat-cache@4.0.1: + dependencies: + flatted: 3.4.4 + keyv: 4.5.4 + + flatted@3.4.4: {} + + for-each@0.3.5: + dependencies: + is-callable: 1.2.7 + + fsevents@2.3.3: + optional: true + + function-bind@1.1.2: {} + + function.prototype.name@1.2.0: + dependencies: + call-bind: 1.0.9 + call-bound: 1.0.4 + es-define-property: 1.0.1 + es-errors: 1.3.0 + functions-have-names: 1.2.3 + has-property-descriptors: 1.0.2 + hasown: 2.0.4 + is-callable: 1.2.7 + is-document.all: 1.0.0 + + functions-have-names@1.2.3: {} + + generator-function@2.0.1: {} + + gensync@1.0.0-beta.2: {} + + get-intrinsic@1.3.0: + dependencies: + call-bind-apply-helpers: 1.0.2 + es-define-property: 1.0.1 + es-errors: 1.3.0 + es-object-atoms: 1.1.2 + function-bind: 1.1.2 + get-proto: 1.0.1 + gopd: 1.2.0 + has-symbols: 1.1.0 + hasown: 2.0.4 + math-intrinsics: 1.1.0 + + get-proto@1.0.1: + dependencies: + dunder-proto: 1.0.1 + es-object-atoms: 1.1.2 + + get-symbol-description@1.1.0: + dependencies: + call-bound: 1.0.4 + es-errors: 1.3.0 + get-intrinsic: 1.3.0 + + glob-parent@6.0.2: + dependencies: + is-glob: 4.0.3 + + globals@14.0.0: {} + + globals@17.11.0: {} + + globalthis@1.0.4: + dependencies: + define-properties: 1.2.1 + gopd: 1.2.0 + + gopd@1.2.0: {} + + has-bigints@1.1.0: {} + + has-flag@4.0.0: {} + + has-property-descriptors@1.0.2: + dependencies: + es-define-property: 1.0.1 + + has-proto@1.2.0: + dependencies: + dunder-proto: 1.0.1 + + has-symbols@1.1.0: {} + + has-tostringtag@1.0.2: + dependencies: + has-symbols: 1.1.0 + + hasown@2.0.4: + dependencies: + function-bind: 1.1.2 + + hermes-estree@0.25.1: {} + + hermes-parser@0.25.1: + dependencies: + hermes-estree: 0.25.1 + + ieee754@1.2.1: {} + + ignore@5.3.2: {} + + ignore@7.0.6: {} + + import-fresh@3.3.1: + dependencies: + parent-module: 1.0.1 + resolve-from: 4.0.0 + + imurmurhash@0.1.4: {} + + internal-slot@1.1.0: + dependencies: + es-errors: 1.3.0 + hasown: 2.0.4 + side-channel: 1.1.1 + + is-array-buffer@3.0.5: + dependencies: + call-bind: 1.0.9 + call-bound: 1.0.4 + get-intrinsic: 1.3.0 + + is-async-function@2.1.1: + dependencies: + async-function: 1.0.0 + call-bound: 1.0.4 + get-proto: 1.0.1 + has-tostringtag: 1.0.2 + safe-regex-test: 1.1.0 + + is-bigint@1.1.0: + dependencies: + has-bigints: 1.1.0 + + is-boolean-object@1.2.2: + dependencies: + call-bound: 1.0.4 + has-tostringtag: 1.0.2 + + is-callable@1.2.7: {} + + is-core-module@2.16.2: + dependencies: + hasown: 2.0.4 + + is-data-view@1.0.2: + dependencies: + call-bound: 1.0.4 + get-intrinsic: 1.3.0 + is-typed-array: 1.1.15 + + is-date-object@1.1.0: + dependencies: + call-bound: 1.0.4 + has-tostringtag: 1.0.2 + + is-document.all@1.0.0: + dependencies: + call-bound: 1.0.4 + + is-extglob@2.1.1: {} + + is-finalizationregistry@1.1.1: + dependencies: + call-bound: 1.0.4 + + is-generator-function@1.1.2: + dependencies: + call-bound: 1.0.4 + generator-function: 2.0.1 + get-proto: 1.0.1 + has-tostringtag: 1.0.2 + safe-regex-test: 1.1.0 + + is-glob@4.0.3: + dependencies: + is-extglob: 2.1.1 + + is-map@2.0.3: {} + + is-negative-zero@2.0.3: {} + + is-number-object@1.1.1: + dependencies: + call-bound: 1.0.4 + has-tostringtag: 1.0.2 + + is-regex@1.2.1: + dependencies: + call-bound: 1.0.4 + gopd: 1.2.0 + has-tostringtag: 1.0.2 + hasown: 2.0.4 + + is-set@2.0.3: {} + + is-shared-array-buffer@1.0.4: + dependencies: + call-bound: 1.0.4 + + is-string@1.1.1: + dependencies: + call-bound: 1.0.4 + has-tostringtag: 1.0.2 + + is-symbol@1.1.1: + dependencies: + call-bound: 1.0.4 + has-symbols: 1.1.0 + safe-regex-test: 1.1.0 + + is-typed-array@1.1.15: + dependencies: + which-typed-array: 1.1.22 + + is-weakmap@2.0.2: {} + + is-weakref@1.1.1: + dependencies: + call-bound: 1.0.4 + + is-weakset@2.0.4: + dependencies: + call-bound: 1.0.4 + get-intrinsic: 1.3.0 + + isarray@2.0.5: {} + + isexe@2.0.0: {} + + iterator.prototype@1.1.5: + dependencies: + define-data-property: 1.1.4 + es-object-atoms: 1.1.2 + get-intrinsic: 1.3.0 + get-proto: 1.0.1 + has-symbols: 1.1.0 + set-function-name: 2.0.2 + + its-fine@2.0.0(@types/react@19.2.18)(react@19.2.8): + dependencies: + '@types/react-reconciler': 0.28.9(@types/react@19.2.18) + react: 19.2.8 + transitivePeerDependencies: + - '@types/react' + + js-tokens@4.0.0: {} + + js-yaml@4.3.1: + dependencies: + argparse: 2.0.1 + + jsesc@3.1.0: {} + + json-buffer@3.0.1: {} + + json-schema-traverse@0.4.1: {} + + json-stable-stringify-without-jsonify@1.0.1: {} + + json5@1.0.2: + dependencies: + minimist: 1.2.8 + + json5@2.2.3: {} + + jsx-ast-utils@3.3.5: + dependencies: + array-includes: 3.1.9 + array.prototype.flat: 1.3.3 + object.assign: 4.1.7 + object.values: 1.2.1 + + keyv@4.5.4: + dependencies: + json-buffer: 3.0.1 + + levn@0.4.1: + dependencies: + prelude-ls: 1.2.1 + type-check: 0.4.0 + + lightningcss-android-arm64@1.33.0: + optional: true + + lightningcss-darwin-arm64@1.33.0: + optional: true + + lightningcss-darwin-x64@1.33.0: + optional: true + + lightningcss-freebsd-x64@1.33.0: + optional: true + + lightningcss-linux-arm-gnueabihf@1.33.0: + optional: true + + lightningcss-linux-arm64-gnu@1.33.0: + optional: true + + lightningcss-linux-arm64-musl@1.33.0: + optional: true + + lightningcss-linux-x64-gnu@1.33.0: + optional: true + + lightningcss-linux-x64-musl@1.33.0: + optional: true + + lightningcss-win32-arm64-msvc@1.33.0: + optional: true + + lightningcss-win32-x64-msvc@1.33.0: + optional: true + + lightningcss@1.33.0: + dependencies: + detect-libc: 2.1.2 + optionalDependencies: + lightningcss-android-arm64: 1.33.0 + lightningcss-darwin-arm64: 1.33.0 + lightningcss-darwin-x64: 1.33.0 + lightningcss-freebsd-x64: 1.33.0 + lightningcss-linux-arm-gnueabihf: 1.33.0 + lightningcss-linux-arm64-gnu: 1.33.0 + lightningcss-linux-arm64-musl: 1.33.0 + lightningcss-linux-x64-gnu: 1.33.0 + lightningcss-linux-x64-musl: 1.33.0 + lightningcss-win32-arm64-msvc: 1.33.0 + lightningcss-win32-x64-msvc: 1.33.0 + + locate-path@6.0.0: + dependencies: + p-locate: 5.0.0 + + lodash.merge@4.6.2: {} + + loose-envify@1.4.0: + dependencies: + js-tokens: 4.0.0 + + lru-cache@5.1.1: + dependencies: + yallist: 3.1.1 + + maath@0.10.8(@types/three@0.182.0)(three@0.182.0): + dependencies: + '@types/three': 0.182.0 + three: 0.182.0 + + magic-string@0.30.21: + dependencies: + '@jridgewell/sourcemap-codec': 1.5.5 + + math-intrinsics@1.1.0: {} + + meshoptimizer@0.22.0: {} + + minimatch@10.2.6: + dependencies: + brace-expansion: 5.0.9 + + minimatch@3.1.5: + dependencies: + brace-expansion: 1.1.18 + + minimist@1.2.8: {} + + ms@2.1.3: {} + + n8ao@2.0.1(postprocessing@6.39.4(three@0.182.0))(three@0.182.0): + dependencies: + postprocessing: 6.39.4(three@0.182.0) + three: 0.182.0 + + nanoid@3.3.18: {} + + natural-compare@1.4.0: {} + + node-exports-info@1.6.2: + dependencies: + array.prototype.flatmap: 1.3.3 + es-errors: 1.3.0 + object.entries: 1.1.9 + semver: 6.3.1 + + node-releases@2.0.53: {} + + object-assign@4.1.1: {} + + object-inspect@1.13.4: {} + + object-keys@1.1.1: {} + + object.assign@4.1.7: + dependencies: + call-bind: 1.0.9 + call-bound: 1.0.4 + define-properties: 1.2.1 + es-object-atoms: 1.1.2 + has-symbols: 1.1.0 + object-keys: 1.1.1 + + object.entries@1.1.9: + dependencies: + call-bind: 1.0.9 + call-bound: 1.0.4 + define-properties: 1.2.1 + es-object-atoms: 1.1.2 + + object.fromentries@2.0.8: + dependencies: + call-bind: 1.0.9 + define-properties: 1.2.1 + es-abstract: 1.24.2 + es-object-atoms: 1.1.2 + + object.groupby@1.0.3: + dependencies: + call-bind: 1.0.9 + define-properties: 1.2.1 + es-abstract: 1.24.2 + + object.values@1.2.1: + dependencies: + call-bind: 1.0.9 + call-bound: 1.0.4 + define-properties: 1.2.1 + es-object-atoms: 1.1.2 + + obug@2.1.4: {} + + optionator@0.9.4: + dependencies: + deep-is: 0.1.4 + fast-levenshtein: 2.0.6 + levn: 0.4.1 + prelude-ls: 1.2.1 + type-check: 0.4.0 + word-wrap: 1.2.5 + + own-keys@1.0.2: + dependencies: + call-bound: 1.0.4 + get-intrinsic: 1.3.0 + object-keys: 1.1.1 + safe-push-apply: 1.0.0 + + p-limit@3.1.0: + dependencies: + yocto-queue: 0.1.0 + + p-locate@5.0.0: + dependencies: + p-limit: 3.1.0 + + parent-module@1.0.1: + dependencies: + callsites: 3.1.0 + + path-exists@4.0.0: {} + + path-key@3.1.1: {} + + path-parse@1.0.7: {} + + pathe@2.0.3: {} + + picocolors@1.1.1: {} + + picomatch@4.0.5: {} + + possible-typed-array-names@1.1.0: {} + + postcss@8.5.26: + dependencies: + nanoid: 3.3.18 + picocolors: 1.1.1 + source-map-js: 1.2.1 + + postprocessing@6.39.4(three@0.182.0): + dependencies: + three: 0.182.0 + + prelude-ls@1.2.1: {} + + prettier-linter-helpers@1.0.1: + dependencies: + fast-diff: 1.3.0 + + prettier@3.9.6: {} + + prop-types@15.8.1: + dependencies: + loose-envify: 1.4.0 + object-assign: 4.1.1 + react-is: 16.13.1 + + punycode@2.3.1: {} + + react-is@16.13.1: {} + + react-use-measure@2.1.7(react@19.2.8): + dependencies: + react: 19.2.8 + + react@19.2.8: {} + + reflect.getprototypeof@1.0.10: + dependencies: + call-bind: 1.0.9 + define-properties: 1.2.1 + es-abstract: 1.24.2 + es-errors: 1.3.0 + es-object-atoms: 1.1.2 + get-intrinsic: 1.3.0 + get-proto: 1.0.1 + which-builtin-type: 1.2.1 + + regexp.prototype.flags@1.5.4: + dependencies: + call-bind: 1.0.9 + define-properties: 1.2.1 + es-errors: 1.3.0 + get-proto: 1.0.1 + gopd: 1.2.0 + set-function-name: 2.0.2 + + resolve-from@4.0.0: {} + + resolve@2.0.0-next.7: + dependencies: + es-errors: 1.3.0 + is-core-module: 2.16.2 + node-exports-info: 1.6.2 + object-keys: 1.1.1 + path-parse: 1.0.7 + supports-preserve-symlinks-flag: 1.0.0 + + rolldown@1.2.4: + dependencies: + '@oxc-project/types': 0.144.0 + '@rolldown/pluginutils': 1.0.1 + optionalDependencies: + '@rolldown/binding-android-arm64': 1.2.4 + '@rolldown/binding-darwin-arm64': 1.2.4 + '@rolldown/binding-darwin-x64': 1.2.4 + '@rolldown/binding-freebsd-x64': 1.2.4 + '@rolldown/binding-linux-arm-gnueabihf': 1.2.4 + '@rolldown/binding-linux-arm64-gnu': 1.2.4 + '@rolldown/binding-linux-arm64-musl': 1.2.4 + '@rolldown/binding-linux-ppc64-gnu': 1.2.4 + '@rolldown/binding-linux-s390x-gnu': 1.2.4 + '@rolldown/binding-linux-x64-gnu': 1.2.4 + '@rolldown/binding-linux-x64-musl': 1.2.4 + '@rolldown/binding-openharmony-arm64': 1.2.4 + '@rolldown/binding-win32-arm64-msvc': 1.2.4 + '@rolldown/binding-win32-x64-msvc': 1.2.4 + + safe-array-concat@1.1.4: + dependencies: + call-bind: 1.0.9 + call-bound: 1.0.4 + get-intrinsic: 1.3.0 + has-symbols: 1.1.0 + isarray: 2.0.5 + + safe-push-apply@1.0.0: + dependencies: + es-errors: 1.3.0 + isarray: 2.0.5 + + safe-regex-test@1.1.0: + dependencies: + call-bound: 1.0.4 + es-errors: 1.3.0 + is-regex: 1.2.1 + + scheduler@0.27.0: {} + + semver@6.3.1: {} + + semver@7.8.5: {} + + set-function-length@1.2.2: + dependencies: + define-data-property: 1.1.4 + es-errors: 1.3.0 + function-bind: 1.1.2 + get-intrinsic: 1.3.0 + gopd: 1.2.0 + has-property-descriptors: 1.0.2 + + set-function-name@2.0.2: + dependencies: + define-data-property: 1.1.4 + es-errors: 1.3.0 + functions-have-names: 1.2.3 + has-property-descriptors: 1.0.2 + + set-proto@1.0.0: + dependencies: + dunder-proto: 1.0.1 + es-errors: 1.3.0 + es-object-atoms: 1.1.2 + + shebang-command@2.0.0: + dependencies: + shebang-regex: 3.0.0 + + shebang-regex@3.0.0: {} + + side-channel-list@1.0.1: + dependencies: + es-errors: 1.3.0 + object-inspect: 1.13.4 + + side-channel-map@1.0.1: + dependencies: + call-bound: 1.0.4 + es-errors: 1.3.0 + get-intrinsic: 1.3.0 + object-inspect: 1.13.4 + + side-channel-weakmap@1.0.2: + dependencies: + call-bound: 1.0.4 + es-errors: 1.3.0 + get-intrinsic: 1.3.0 + object-inspect: 1.13.4 + side-channel-map: 1.0.1 + + side-channel@1.1.1: + dependencies: + es-errors: 1.3.0 + object-inspect: 1.13.4 + side-channel-list: 1.0.1 + side-channel-map: 1.0.1 + side-channel-weakmap: 1.0.2 + + siginfo@2.0.0: {} + + source-map-js@1.2.1: {} + + stackback@0.0.2: {} + + std-env@4.2.0: {} + + stop-iteration-iterator@1.1.0: + dependencies: + es-errors: 1.3.0 + internal-slot: 1.1.0 + + string.prototype.matchall@4.0.12: + dependencies: + call-bind: 1.0.9 + call-bound: 1.0.4 + define-properties: 1.2.1 + es-abstract: 1.24.2 + es-errors: 1.3.0 + es-object-atoms: 1.1.2 + get-intrinsic: 1.3.0 + gopd: 1.2.0 + has-symbols: 1.1.0 + internal-slot: 1.1.0 + regexp.prototype.flags: 1.5.4 + set-function-name: 2.0.2 + side-channel: 1.1.1 + + string.prototype.repeat@1.0.0: + dependencies: + define-properties: 1.2.1 + es-abstract: 1.24.2 + + string.prototype.trim@1.2.11: + dependencies: + call-bind: 1.0.9 + call-bound: 1.0.4 + define-data-property: 1.1.4 + define-properties: 1.2.1 + es-abstract: 1.24.2 + es-object-atoms: 1.1.2 + has-property-descriptors: 1.0.2 + safe-regex-test: 1.1.0 + + string.prototype.trimend@1.0.10: + dependencies: + call-bind: 1.0.9 + call-bound: 1.0.4 + define-properties: 1.2.1 + es-object-atoms: 1.1.2 + + string.prototype.trimstart@1.0.8: + dependencies: + call-bind: 1.0.9 + define-properties: 1.2.1 + es-object-atoms: 1.1.2 + + strip-bom@3.0.0: {} + + strip-json-comments@3.1.1: {} + + supports-color@7.2.0: + dependencies: + has-flag: 4.0.0 + + supports-preserve-symlinks-flag@1.0.0: {} + + suspend-react@0.1.3(react@19.2.8): + dependencies: + react: 19.2.8 + + synckit@0.11.13: + dependencies: + '@pkgr/core': 0.3.6 + + three@0.182.0: {} + + tinybench@2.9.0: {} + + tinyexec@1.3.0: {} + + tinyglobby@0.2.17: + dependencies: + fdir: 6.5.0(picomatch@4.0.5) + picomatch: 4.0.5 + + tinyrainbow@3.1.1: {} + + ts-api-utils@2.5.0(typescript@6.0.3): + dependencies: + typescript: 6.0.3 + + tsconfig-paths@3.15.0: + dependencies: + '@types/json5': 0.0.29 + json5: 1.0.2 + minimist: 1.2.8 + strip-bom: 3.0.0 + + type-check@0.4.0: + dependencies: + prelude-ls: 1.2.1 + + typed-array-buffer@1.0.3: + dependencies: + call-bound: 1.0.4 + es-errors: 1.3.0 + is-typed-array: 1.1.15 + + typed-array-byte-length@1.0.3: + dependencies: + call-bind: 1.0.9 + for-each: 0.3.5 + gopd: 1.2.0 + has-proto: 1.2.0 + is-typed-array: 1.1.15 + + typed-array-byte-offset@1.0.4: + dependencies: + available-typed-arrays: 1.0.7 + call-bind: 1.0.9 + for-each: 0.3.5 + gopd: 1.2.0 + has-proto: 1.2.0 + is-typed-array: 1.1.15 + reflect.getprototypeof: 1.0.10 + + typed-array-length@1.0.8: + dependencies: + call-bind: 1.0.9 + for-each: 0.3.5 + gopd: 1.2.0 + is-typed-array: 1.1.15 + possible-typed-array-names: 1.1.0 + reflect.getprototypeof: 1.0.10 + + typescript@6.0.3: {} + + unbox-primitive@1.1.0: + dependencies: + call-bound: 1.0.4 + has-bigints: 1.1.0 + has-symbols: 1.1.0 + which-boxed-primitive: 1.1.1 + + undici-types@8.3.0: {} + + update-browserslist-db@1.3.1(browserslist@4.28.8): + dependencies: + browserslist: 4.28.8 + escalade: 3.2.0 + picocolors: 1.1.1 + + uri-js@4.4.1: + dependencies: + punycode: 2.3.1 + + use-sync-external-store@1.6.0(react@19.2.8): + dependencies: + react: 19.2.8 + + vite@8.2.1(@types/node@26.2.0): + dependencies: + lightningcss: 1.33.0 + picomatch: 4.0.5 + postcss: 8.5.26 + rolldown: 1.2.4 + tinyglobby: 0.2.17 + optionalDependencies: + '@types/node': 26.2.0 + fsevents: 2.3.3 + + vitest@4.1.10(@types/node@26.2.0)(vite@8.2.1(@types/node@26.2.0)): + dependencies: + '@vitest/expect': 4.1.10 + '@vitest/mocker': 4.1.10(vite@8.2.1(@types/node@26.2.0)) + '@vitest/pretty-format': 4.1.10 + '@vitest/runner': 4.1.10 + '@vitest/snapshot': 4.1.10 + '@vitest/spy': 4.1.10 + '@vitest/utils': 4.1.10 + es-module-lexer: 2.3.1 + expect-type: 1.4.0 + magic-string: 0.30.21 + obug: 2.1.4 + pathe: 2.0.3 + picomatch: 4.0.5 + std-env: 4.2.0 + tinybench: 2.9.0 + tinyexec: 1.3.0 + tinyglobby: 0.2.17 + tinyrainbow: 3.1.1 + vite: 8.2.1(@types/node@26.2.0) + why-is-node-running: 2.3.0 + optionalDependencies: + '@types/node': 26.2.0 + transitivePeerDependencies: + - msw + + which-boxed-primitive@1.1.1: + dependencies: + is-bigint: 1.1.0 + is-boolean-object: 1.2.2 + is-number-object: 1.1.1 + is-string: 1.1.1 + is-symbol: 1.1.1 + + which-builtin-type@1.2.1: + dependencies: + call-bound: 1.0.4 + function.prototype.name: 1.2.0 + has-tostringtag: 1.0.2 + is-async-function: 2.1.1 + is-date-object: 1.1.0 + is-finalizationregistry: 1.1.1 + is-generator-function: 1.1.2 + is-regex: 1.2.1 + is-weakref: 1.1.1 + isarray: 2.0.5 + which-boxed-primitive: 1.1.1 + which-collection: 1.0.2 + which-typed-array: 1.1.22 + + which-collection@1.0.2: + dependencies: + is-map: 2.0.3 + is-set: 2.0.3 + is-weakmap: 2.0.2 + is-weakset: 2.0.4 + + which-typed-array@1.1.22: + dependencies: + available-typed-arrays: 1.0.7 + call-bind: 1.0.9 + call-bound: 1.0.4 + for-each: 0.3.5 + get-proto: 1.0.1 + gopd: 1.2.0 + has-tostringtag: 1.0.2 + + which@2.0.2: + dependencies: + isexe: 2.0.0 + + why-is-node-running@2.3.0: + dependencies: + siginfo: 2.0.0 + stackback: 0.0.2 + + word-wrap@1.2.5: {} + + yallist@3.1.1: {} + + yocto-queue@0.1.0: {} + + zod-validation-error@4.0.2(zod@4.4.3): + dependencies: + zod: 4.4.3 + + zod@4.4.3: {} + + zustand@5.0.15(@types/react@19.2.18)(react@19.2.8)(use-sync-external-store@1.6.0(react@19.2.8)): + optionalDependencies: + '@types/react': 19.2.18 + react: 19.2.8 + use-sync-external-store: 1.6.0(react@19.2.8) diff --git a/src/EffectComposer.tsx b/src/EffectComposer.tsx index 7b0fef88..1f48a149 100644 --- a/src/EffectComposer.tsx +++ b/src/EffectComposer.tsx @@ -1,5 +1,6 @@ -import { useFrame, useThree, type Instance } from '@react-three/fiber' +import { useFrame, useThree } from '@react-three/fiber' import { + CopyPass, DepthDownsamplingPass, Effect, EffectAttribute, @@ -16,13 +17,15 @@ import { useImperativeHandle, useLayoutEffect, useMemo, + useReducer, useRef, useState, type ReactNode, type Ref, } from 'react' import type { Camera, Group, Scene, TextureDataType, WebGLRenderer } from 'three' -import { HalfFloatType, NoToneMapping } from 'three' +import { HalfFloatType, NoToneMapping, Vector2 } from 'three' +import { readGroupChildren, updateIfChanged } from './util' export const EffectComposerContext = /* @__PURE__ */ createContext<{ composer: EffectComposerImpl @@ -31,6 +34,11 @@ export const EffectComposerContext = /* @__PURE__ */ createContext<{ camera: Camera scene: Scene resolutionScale?: number + // A child's local state update doesn't re-render its parent - descendants + // that add/remove a bare Pass of their own (e.g. EffectGroup) call this + // to make the tree walk below notice. + requestRebuild: () => void + autoClear: boolean }>(null!) export type EffectComposerProps = { @@ -41,12 +49,15 @@ export type EffectComposerProps = { enableNormalPass?: boolean stencilBuffer?: boolean autoClear?: boolean + autoRenderToScreen?: boolean resolutionScale?: number multisampling?: number frameBufferType?: TextureDataType renderPriority?: number camera?: Camera scene?: Scene + renderPass?: (scene: Scene, camera: Camera) => Pass + mergeMode?: 'auto' | 'all' | 'none' ref?: Ref } @@ -59,11 +70,8 @@ type ComposerState = { const isConvolution = (effect: Effect): boolean => (effect.getAttributes() & EffectAttribute.CONVOLUTION) === EffectAttribute.CONVOLUTION -/** - * autoClear/toneMapping get force-set and never restored by whoever sets - * them. Ref-counted per (renderer, property) since composers can share a - * renderer; skips restoring if the value already changed since acquire. - */ +// autoClear/toneMapping get force-set and never restored. Ref-counted per +// (renderer, property) since composers can share a renderer. function createRendererPropertyGuard(property: K) { const refs = new WeakMap< WebGLRenderer, @@ -97,12 +105,48 @@ function createRendererPropertyGuard(prop const autoClearGuard = /* @__PURE__ */ createRendererPropertyGuard('autoClear') const toneMappingGuard = /* @__PURE__ */ createRendererPropertyGuard('toneMapping') -/** - * Groups a flat, ordered list of Effect/Pass instances into actual composer - * passes, merging consecutive non-convolution Effects into a single - * EffectPass. - */ -function buildPasses(nodes: Array, camera: Camera): Pass[] { +// Scratch vector for gl.getSize() below - written then read synchronously +// within the same call, never held onto across a render/frame, so sharing +// one instance across every is safe (same reasoning as +// DEFAULT_SCREEN_RES in LensFlare.tsx: safe because nothing ever aliases it). +const glSize = /* @__PURE__ */ new Vector2() + +// Stable identity for the `renderPass` default - it's in the creation +// effect's dependency array below (changing it recreates the composer, +// same as depthBuffer/multisampling), so a fresh arrow function every +// render here would recreate the composer on every render too. +const defaultRenderPass = (scene: Scene, camera: Camera): Pass => new RenderPass(scene, camera) + +// Only passes buildPasses itself constructs - not a user's own bare Pass, +// which owns its own lifecycle. +const generatedPasses = /* @__PURE__ */ new WeakSet() + +// EffectGroup registers its pass here (see EffectGroup.tsx) so the rebuild +// effect below knows to add a shared trailing CopyPass. +export const groupPasses = /* @__PURE__ */ new WeakSet() + +// Not pass.dispose() - EffectPass.dispose() also disposes the effects it +// wraps, which have their own lifecycle. setEffects([]) detaches them first. +export function disposePassWithoutEffects(pass: Pass): void { + if (pass instanceof EffectPass) { + ;(pass as unknown as { setEffects(effects: never[]): void }).setEffects([]) + } + Pass.prototype.dispose.call(pass) +} + +function disposeGeneratedPass(pass: Pass): void { + if (!generatedPasses.has(pass)) return + disposePassWithoutEffects(pass) +} + +// 'auto' (default): consecutive Effects share one EffectPass, same as vanilla +// postprocessing allows by hand - a run may contain at most one convolution +// Effect (e.g. DepthOfField), since the library throws if two land in the +// same pass. 'all' merges through that limit too, same no-guardrail +// treatment EffectGroup already gives its own children - multiple +// convolution Effects in one run will throw at render time. 'none' gives +// every Effect its own EffectPass. +function buildPasses(nodes: Array, camera: Camera, mergeMode: 'auto' | 'all' | 'none'): Pass[] { const passes: Pass[] = [] for (let i = 0; i < nodes.length; i++) { @@ -110,17 +154,22 @@ function buildPasses(nodes: Array, camera: Camera): Pass[] { if (node instanceof Effect) { const effects: Effect[] = [node] + let hasConvolution = isConvolution(node) - if (!isConvolution(node)) { + if (mergeMode !== 'none') { let next: Effect | Pass | undefined while ((next = nodes[i + 1]) instanceof Effect) { - if (isConvolution(next)) break + const nextIsConvolution = isConvolution(next) + if (mergeMode === 'auto' && hasConvolution && nextIsConvolution) break effects.push(next) + hasConvolution ||= nextIsConvolution i++ } } - passes.push(new EffectPass(camera, ...effects)) + const pass = new EffectPass(camera, ...effects) + generatedPasses.add(pass) + passes.push(pass) } else if (node instanceof Pass) { passes.push(node) } @@ -137,27 +186,35 @@ export const EffectComposer = /* @__PURE__ */ memo(function EffectComposer({ enabled = true, renderPriority = 1, autoClear = true, + autoRenderToScreen = true, depthBuffer, enableNormalPass, stencilBuffer, multisampling = 8, frameBufferType = HalfFloatType, + renderPass = defaultRenderPass, + mergeMode = 'auto', ref, }: EffectComposerProps) { - const { gl, scene: defaultScene, camera: defaultCamera, size } = useThree() + const { gl, scene: defaultScene, camera: defaultCamera } = useThree() const scene = _scene || defaultScene const camera = _camera || defaultCamera - // EffectComposer owns WebGL resources, so it must be created and - // disposed inside an effect lifecycle. useMemo is not suitable here - // because React may discard memoized values without running cleanup. + gl.getSize(glSize) + + // useMemo can't own WebGL resources - React may discard it without cleanup. const [composerState, setComposerState] = useState(null) + // Dispatch is stable across renders, so it never destabilizes `state` below. + const [, requestRebuild] = useReducer((c: number) => c + 1, 0) + useEffect(() => { autoClearGuard.acquire(gl, false) const effectComposer = new EffectComposerImpl(gl, { depthBuffer, stencilBuffer, multisampling, frameBufferType }) - effectComposer.addPass(new RenderPass(scene, camera)) + + effectComposer.autoRenderToScreen = autoRenderToScreen + effectComposer.addPass(renderPass(scene, camera)) let normalPass: NormalPass | null = null let downSamplingPass: DepthDownsamplingPass | null = null @@ -174,27 +231,51 @@ export const EffectComposer = /* @__PURE__ */ memo(function EffectComposer({ } } - effectComposer.setSize(size.width, size.height) + effectComposer.setSize(glSize.width, glSize.height) setComposerState({ composer: effectComposer, normalPass, downSamplingPass }) return () => { + // The rebuild effect below may not have detached its passes yet + // (composerState only updates next render) - without this, dispose() + // would kill effects the new composer is about to reuse. + for (const pass of effectComposer.passes) disposeGeneratedPass(pass) effectComposer.dispose() autoClearGuard.release(gl) } - // `size` intentionally excluded: it's applied via the composer.setSize + // `glSize` intentionally excluded: it's applied via the composer.setSize // effect below, and shouldn't tear down/recreate the whole composer. // eslint-disable-next-line react-hooks/exhaustive-deps - }, [camera, gl, depthBuffer, stencilBuffer, multisampling, frameBufferType, scene, enableNormalPass, resolutionScale]) - - useEffect(() => { - composerState?.composer.setSize(size.width, size.height) - }, [composerState, size]) + }, [ + camera, + gl, + depthBuffer, + stencilBuffer, + multisampling, + frameBufferType, + autoRenderToScreen, + renderPass, + scene, + enableNormalPass, + resolutionScale, + ]) + + // Last size actually applied to the composer, so the check below is a + // cheap no-op on frames where nothing changed. + const appliedSizeRef = useRef({ width: -1, height: -1 }) useFrame( (_, delta) => { if (!enabled || !composerState) return const { composer } = composerState + + gl.getSize(glSize) + if (glSize.width !== appliedSizeRef.current.width || glSize.height !== appliedSizeRef.current.height) { + composer.setSize(glSize.width, glSize.height) + appliedSizeRef.current.width = glSize.width + appliedSizeRef.current.height = glSize.height + } + const currentAutoClear = gl.autoClear gl.autoClear = autoClear if (stencilBuffer && !autoClear) gl.clearStencil() @@ -204,24 +285,39 @@ export const EffectComposer = /* @__PURE__ */ memo(function EffectComposer({ enabled ? renderPriority : 0 ) - // Passes are derived from the actual r3f scene graph rather than tracked - // incrementally, so the list always matches current JSX order — including - // through wrapper components — even after a reorder or a remount. + // Derived from the r3f scene graph (not tracked incrementally) so order + // always matches JSX, even through wrapper components or a reorder. const group = useRef(null!) + const nodesRef = useRef>([]) + const [nodesVersion, setNodesVersion] = useState(0) + // Runs every render, but only bumps nodesVersion (triggering the rebuild + // below) when the resolved node list actually changed. + useLayoutEffect(() => { + if (!composerState) return + const nodes = readGroupChildren( + group.current, + (object): object is Effect | Pass => object instanceof Effect || object instanceof Pass + ) + if (updateIfChanged(nodesRef, nodes)) setNodesVersion((v) => v + 1) + }) + + // Only re-runs when nodesVersion/composerState/camera change - React's + // own dependency bailout, so create/cleanup pairing stays correct. useLayoutEffect(() => { if (!composerState) return const { composer, normalPass, downSamplingPass } = composerState - const passes: Pass[] = [] - const groupInstance = (group.current as Group & { __r3f: Instance }).__r3f + const passes = buildPasses(nodesRef.current, camera, mergeMode) - if (groupInstance) { - const nodes = groupInstance.children.map((child) => child.object).filter( - (object): object is Effect | Pass => object instanceof Effect || object instanceof Pass - ) - - passes.push(...buildPasses(nodes, camera)) + // A toggleable pass (EffectGroup) sitting last would leave nothing on + // screen once disabled - postprocessing assigns renderToScreen by + // structural position, not `.enabled`. A shared trailing CopyPass + // (always enabled, always added last) fixes that for the whole chain. + if (passes.some((pass) => groupPasses.has(pass))) { + const trailingCopyPass = new CopyPass() + generatedPasses.add(trailingCopyPass) + passes.push(trailingCopyPass) } for (const pass of passes) composer.addPass(pass) @@ -232,11 +328,14 @@ export const EffectComposer = /* @__PURE__ */ memo(function EffectComposer({ } return () => { - for (const pass of passes) composer.removePass(pass) + for (const pass of passes) { + composer.removePass(pass) + disposeGeneratedPass(pass) + } if (normalPass) normalPass.enabled = false if (downSamplingPass) downSamplingPass.enabled = false } - }, [composerState, children, camera]) + }, [composerState, nodesVersion, camera, mergeMode]) // Disable tone mapping because threejs disallows tonemapping on render targets useEffect(() => { @@ -258,9 +357,11 @@ export const EffectComposer = /* @__PURE__ */ memo(function EffectComposer({ resolutionScale, camera, scene, + requestRebuild, + autoClear, } : null, - [composerState, resolutionScale, camera, scene] + [composerState, resolutionScale, camera, scene, requestRebuild, autoClear] ) // Expose the composer diff --git a/src/EffectGroup.tsx b/src/EffectGroup.tsx new file mode 100644 index 00000000..a2a111c0 --- /dev/null +++ b/src/EffectGroup.tsx @@ -0,0 +1,74 @@ +import { Effect, EffectPass } from 'postprocessing' +import { use, useImperativeHandle, useLayoutEffect, useRef, useState, type ReactNode, type Ref } from 'react' +import type { Group } from 'three' +import { disposePassWithoutEffects, EffectComposerContext, groupPasses } from './EffectComposer' +import { readGroupChildren, updateIfChanged } from './util' + +export type EffectGroupProps = { + /** Toggles the whole pass, like Pass.enabled in vanilla postprocessing - cheap, no reconstruction. */ + enabled?: boolean + children: ReactNode + ref?: Ref +} + +// Groups its children into one EffectPass instead of relying on +// EffectComposer's automatic consecutive-effect merging. Renders children +// into a hidden inner (invisible to EffectComposer's own top-level +// walk) and renders the resulting pass back out via at this +// component's own JSX position, so EffectComposer's existing bare-Pass +// passthrough places it correctly relative to sibling effects. +export function EffectGroup({ enabled = true, children, ref }: EffectGroupProps) { + const { camera, requestRebuild } = use(EffectComposerContext) + + const group = useRef(null!) + const effectsRef = useRef([]) + const [pass, setPass] = useState(null) + + // Mirrors EffectComposer's own buildPasses: always constructs a fresh + // EffectPass when the effect list changes, rather than updating one in + // place - so composer.addPass() runs its normal setSize/initialize dance + // for every effect, with nothing to replicate by hand here. + useLayoutEffect(() => { + const effects = readGroupChildren(group.current, (object): object is Effect => object instanceof Effect) + if (!updateIfChanged(effectsRef, effects)) return + + if (!effects.length) { + setPass(null) + return + } + + const newPass = new EffectPass(camera, ...effects) + groupPasses.add(newPass) + setPass(newPass) + }) + + // useLayoutEffect (not useEffect) so `enabled` is applied before + // EffectComposer's own effect adds this pass to the composer. + useLayoutEffect(() => { + if (pass) pass.enabled = enabled + }, [pass, enabled]) + + // A local state update above doesn't re-render EffectComposer, so its own + // tree walk would never notice this pass appearing/disappearing. + useLayoutEffect(() => { + requestRebuild() + }, [pass, requestRebuild]) + + // Disposes whichever pass this replaces (or the last one, on unmount) - + // the only place a pass gets disposed, so there's no double-dispose risk + // from also doing it inline above where `pass` is replaced. + useLayoutEffect(() => { + return () => { + if (pass) disposePassWithoutEffects(pass) + } + }, [pass]) + + useImperativeHandle(ref, () => pass as EffectPass, [pass]) + + return ( + <> + {children} + {pass && } + + ) +} diff --git a/src/createEffectComponent.tsx b/src/createEffectComponent.tsx new file mode 100644 index 00000000..ae807fe4 --- /dev/null +++ b/src/createEffectComponent.tsx @@ -0,0 +1,71 @@ +import { extend, useThree } from '@react-three/fiber' +import type { BlendFunction, Effect, Pass } from 'postprocessing' +import type { ExoticComponent, JSX, Ref } from 'react' +import { useRef } from 'react' +import { useLiveDefaults, useMergeRefs } from './util' + +export type EffectConstructor = new (...args: any[]) => Effect | Pass + +// The effect's own options type, straight off its constructor - postprocessing +// already types every effect's sole options object precisely (either inline or, +// like BloomEffect, as a named exported type); this just strips the `| undefined` +// that comes from the parameter being optional. +export type EffectOptions = NonNullable[0]> + +const components = new WeakMap | string>() +let i = 0 + +const BLEND_KEYS = ['blendMode-blendFunction', 'blendMode-opacity-value'] + +/** + * Registers `effect` as a JSX intrinsic once per class and returns a + * component that renders it. Everything else - construction from `args`, + * live prop application (with the same Color/Vector coercion and reset- + * to-default on removal any r3f element gets), disposal - is r3f's own + * reconciler, same rules as ``/``. Only fits + * effects whose constructor works with zero arguments (`new Effect()`) - + * r3f's own reset-on-removal falls back to `0` otherwise, which is wrong + * for anything non-numeric. Effects that require e.g. scene/camera stay + * hand-rolled (see Outline.tsx, SelectiveBloom.tsx, ShockWave.tsx). + * + * `blendFunction`/`opacity` are pierced through to `blendMode-*` - every + * `Effect` has them on a nested `blendMode`, not on the effect itself, so a + * plain top-level prop would silently land on a stray, unread property. + * Applied via useLiveDefaults, not as plain JSX props: BlendMode's own + * constructor requires `blendFunction` (no default), so its constructor + * length isn't 0 either, and r3f's native reset-on-removal falls back to + * `changedProps[prop] = 0` - which is BlendFunction.SKIP, not a merely + * "wrong" blend function but one that hides the effect entirely. + */ +export function createEffectComponent( + effect: T +): ( + props: P & { + blendFunction?: BlendFunction + opacity?: number + args?: ConstructorParameters + ref?: Ref> + } +) => JSX.Element { + return function EffectComponent({ blendFunction, opacity, ref, ...props }: any) { + let Component = components.get(effect) + + if (!Component) { + const key = `@react-three/postprocessing/${effect.name}-${i++}` + extend({ [key]: effect }) + components.set(effect, (Component = key)) + } + + const camera = useThree((state) => state.camera) + const localRef = useRef>(null) + const setRef = useMergeRefs(localRef, ref) + + useLiveDefaults( + localRef, + { 'blendMode-blendFunction': blendFunction, 'blendMode-opacity-value': opacity }, + BLEND_KEYS + ) + + return + } +} diff --git a/src/effects/ASCII.tsx b/src/effects/ASCII.tsx index b6744b56..2889dd39 100644 --- a/src/effects/ASCII.tsx +++ b/src/effects/ASCII.tsx @@ -2,9 +2,9 @@ // https://twitter.com/emilwidlund/status/1652386482420609024 import { Effect } from 'postprocessing' -import { Ref, useMemo } from 'react' -import { CanvasTexture, Color, NearestFilter, RepeatWrapping, Texture, Uniform } from 'three' -import { useDispose } from '../util' +import type { Ref } from 'react' +import { CanvasTexture, Color, type ColorRepresentation, NearestFilter, RepeatWrapping, Texture, Uniform } from 'three' +import { createEffectComponent } from '../createEffectComponent' const fragment = /* glsl */ ` uniform sampler2D uCharacters; @@ -47,17 +47,21 @@ const fragment = /* glsl */ ` } ` -interface IASCIIEffectProps { +export type ASCIIProps = { font?: string characters?: string fontSize?: number cellSize?: number - color?: string + color?: ColorRepresentation invert?: boolean ref?: Ref } class ASCIIEffect extends Effect { + private _font: string + private _characters: string + private _fontSize: number + constructor({ font = 'arial', characters = ` .:,'-^=*+?!|0#X%WM@`, @@ -65,7 +69,7 @@ class ASCIIEffect extends Effect { cellSize = 16, color = '#ffffff', invert = false, - }: Omit = {}) { + }: Omit = {}) { const uniforms = new Map([ ['uCharacters', new Uniform(new Texture())], ['uCellSize', new Uniform(cellSize)], @@ -76,11 +80,71 @@ class ASCIIEffect extends Effect { super('ASCIIEffect', fragment, { uniforms }) - const charactersTextureUniform = this.uniforms.get('uCharacters') + this._font = font + this._characters = characters + this._fontSize = fontSize + this.updateCharactersTexture() + } - if (charactersTextureUniform) { - charactersTextureUniform.value = this.createCharactersTexture(characters, font, fontSize) - } + get cellSize(): number { + return this.uniforms.get('uCellSize')!.value + } + + set cellSize(value: number) { + this.uniforms.get('uCellSize')!.value = value + } + + get invert(): boolean { + return this.uniforms.get('uInvert')!.value + } + + set invert(value: boolean) { + this.uniforms.get('uInvert')!.value = value + } + + get color(): Color { + return this.uniforms.get('uColor')!.value + } + + set color(value: ColorRepresentation) { + this.uniforms.get('uColor')!.value.set(value) + } + + get font(): string { + return this._font + } + + set font(value: string) { + this._font = value + this.updateCharactersTexture() + } + + get characters(): string { + return this._characters + } + + set characters(value: string) { + this._characters = value + this.uniforms.get('uCharactersCount')!.value = value.length + this.updateCharactersTexture() + } + + get fontSize(): number { + return this._fontSize + } + + set fontSize(value: number) { + this._fontSize = value + this.updateCharactersTexture() + } + + // Regenerates the character atlas texture - characters/font/fontSize have + // no cheaper live update path, unlike the plain-uniform props above. + private updateCharactersTexture(): void { + const uniform = this.uniforms.get('uCharacters')! + const previous = uniform.value as Texture + uniform.value = this.createCharactersTexture(this._characters, this._font, this._fontSize) + previous.dispose() } /** Draws the characters on a Canvas and returns a texture */ @@ -116,21 +180,4 @@ class ASCIIEffect extends Effect { } } -export function ASCII({ - font = 'arial', - characters = ` .:,'-^=*+?!|0#X%WM@`, - fontSize = 54, - cellSize = 16, - color = '#ffffff', - invert = false, - ref, -}: IASCIIEffectProps) { - const effect = useMemo( - () => new ASCIIEffect({ characters, font, fontSize, cellSize, color, invert }), - [characters, fontSize, cellSize, color, invert, font] - ) - - useDispose(effect) - - return -} +export const ASCII = /* @__PURE__ */ createEffectComponent(ASCIIEffect) diff --git a/src/effects/Autofocus.tsx b/src/effects/Autofocus.tsx index fefcad56..d061655d 100644 --- a/src/effects/Autofocus.tsx +++ b/src/effects/Autofocus.tsx @@ -1,11 +1,9 @@ import { createPortal, useFrame, useThree, type Vector3 as R3FVector3 } from '@react-three/fiber' import { easing } from 'maath' -import { CopyPass, DepthOfFieldEffect, DepthPickingPass } from 'postprocessing' +import { DepthOfFieldEffect } from 'postprocessing' import { Ref, useCallback, - useContext, - useEffect, useImperativeHandle, useMemo, useRef, @@ -15,27 +13,10 @@ import { } from 'react' import { Mesh, Vector3 } from 'three' -import { EffectComposerContext } from '../EffectComposer' +import { DepthPicking, useDepthPicking, type DepthPickingApi } from '../passes/DepthPicking' import { DepthOfField } from './DepthOfField' -// EffectComposerImpl.dispose() disposes every pass it currently holds — -// including these two, since they're added via composer.addPass below. -// When Autofocus unmounts alongside its ancestor EffectComposer (e.g. a -// full tree unmount), both the composer's own teardown AND this -// component's cleanup effect would dispose the same instances. Wrapping -// dispose here makes it safe no matter which caller gets there first. -function makeDisposeIdempotent void }>(instance: T): T { - let disposed = false - const dispose = instance.dispose.bind(instance) - instance.dispose = () => { - if (disposed) return - disposed = true - dispose() - } - return instance -} - -export type AutofocusProps = ComponentProps & { +export type AutofocusProps = Omit, 'ref'> & { target?: R3FVector3 /** should the target follow the pointer */ mouse?: boolean @@ -64,50 +45,22 @@ export function Autofocus({ ...props }: AutofocusProps) { const dofRef = useRef(null) - const hitpointRef = useRef(null) - const targetRef = useRef(null) + const pickRef = useRef(null) + const getHit = useDepthPicking(pickRef) + const hitpointMarkerRef = useRef(null) + const dofTargetMarkerRef = useRef(null) const scene = useThree(({ scene }) => scene) const pointer = useThree(({ pointer }) => pointer) - const { composer, camera } = useContext(EffectComposerContext) - - // see: https://codesandbox.io/s/depthpickingpass-x130hg - const [depthPickingPass] = useState(() => makeDisposeIdempotent(new DepthPickingPass())) - const [copyPass] = useState(() => makeDisposeIdempotent(new CopyPass())) - useEffect(() => { - composer.addPass(depthPickingPass) - composer.addPass(copyPass) - return () => { - composer.removePass(depthPickingPass) - composer.removePass(copyPass) - } - }, [composer, depthPickingPass, copyPass]) - - useEffect(() => { - return () => { - depthPickingPass.dispose() - copyPass.dispose() - } - }, [depthPickingPass, copyPass]) - const [hitpoint] = useState(() => new Vector3(0, 0, 0)) - - const [ndc] = useState(() => new Vector3(0, 0, 0)) - const getHit = useCallback( - async (x: number, y: number) => { - ndc.x = x - ndc.y = y - ndc.z = await depthPickingPass.readDepth(ndc) - ndc.z = ndc.z * 2.0 - 1.0 - const hit = 1 - ndc.z > 0.0000001 // it is missed if ndc.z is close to 1 - return hit ? ndc.unproject(camera) : false - }, - [ndc, depthPickingPass, camera] - ) + // A stable non-null value, purely to enable DepthOfField's own autoFocus + // mode (`target != null`) - the actual per-frame value is applied + // imperatively to dofRef.current.target below. + const [autoFocusMarker] = useState(() => new Vector3()) + const [hitpoint] = useState(() => new Vector3()) const update = useCallback( async (delta: number, updateTarget = true) => { - // Update hitpoint if (target) { hitpoint.set(...(target as unknown as [number, number, number])) } else { @@ -116,7 +69,6 @@ export function Autofocus({ if (hit) hitpoint.copy(hit) } - // Update target if (updateTarget && dofRef.current?.target) { if (smoothTime > 0 && delta > 0) { easing.damp3(dofRef.current.target, hitpoint, smoothTime, delta) @@ -125,42 +77,37 @@ export function Autofocus({ } } }, - [target, hitpoint, followMouse, getHit, smoothTime, pointer] + [target, hitpoint, followMouse, pointer, getHit, smoothTime] ) - useFrame(async (_, delta) => { + useFrame((_, delta) => { if (!manual) { update(delta) } - if (hitpointRef.current) { - hitpointRef.current.position.copy(hitpoint) + if (hitpointMarkerRef.current) { + hitpointMarkerRef.current.position.copy(hitpoint) } - if (targetRef.current && dofRef.current?.target) { - targetRef.current.position.copy(dofRef.current.target) + if (dofTargetMarkerRef.current && dofRef.current?.target) { + dofTargetMarkerRef.current.position.copy(dofRef.current.target) } }) // Ref API - const api = useMemo( - () => ({ - dofRef, - hitpoint, - update, - }), - [hitpoint, update] - ) + const api = useMemo(() => ({ dofRef, hitpoint, update }), [hitpoint, update]) useImperativeHandle(ref, () => api, [api]) return ( <> + + {debug ? createPortal( <> - + - + @@ -169,7 +116,7 @@ export function Autofocus({ ) : null} - + ) } diff --git a/src/effects/Bloom.tsx b/src/effects/Bloom.tsx index f3c9193b..59833626 100644 --- a/src/effects/Bloom.tsx +++ b/src/effects/Bloom.tsx @@ -1,6 +1,34 @@ import { BlendFunction, BloomEffect } from 'postprocessing' -import { wrapEffect } from '../wrapEffect' +import type { Ref } from 'react' +import { useMemo } from 'react' +import { createEffectComponent, type EffectOptions } from '../createEffectComponent' -export const Bloom = /* @__PURE__ */ wrapEffect(BloomEffect, { - blendFunction: BlendFunction.ADD, -}) +type BloomOptions = EffectOptions + +const BloomImpl = /* @__PURE__ */ createEffectComponent(BloomEffect) + +export type BloomProps = BloomOptions & { opacity?: number; ref?: Ref } + +// luminanceThreshold/luminanceSmoothing/mipmapBlur/radius/levels/resolution* +// have no live setter in postprocessing - routed through args so they still +// work as plain props, just via reconstruction instead of mutation. +export function Bloom({ + blendFunction = BlendFunction.ADD, + luminanceThreshold, + luminanceSmoothing, + mipmapBlur, + radius, + levels, + resolutionScale, + resolutionX, + resolutionY, + ...liveProps +}: BloomProps) { + const args = useMemo<[BloomOptions]>( + () => [ + { luminanceThreshold, luminanceSmoothing, mipmapBlur, radius, levels, resolutionScale, resolutionX, resolutionY }, + ], + [luminanceThreshold, luminanceSmoothing, mipmapBlur, radius, levels, resolutionScale, resolutionX, resolutionY] + ) + return +} diff --git a/src/effects/BrightnessContrast.tsx b/src/effects/BrightnessContrast.tsx index ac1de7b0..cba9939e 100644 --- a/src/effects/BrightnessContrast.tsx +++ b/src/effects/BrightnessContrast.tsx @@ -1,4 +1,7 @@ import { BrightnessContrastEffect } from 'postprocessing' -import { wrapEffect } from '../wrapEffect' +import { createEffectComponent, type EffectOptions } from '../createEffectComponent' -export const BrightnessContrast = /* @__PURE__ */ wrapEffect(BrightnessContrastEffect) +export const BrightnessContrast = /* @__PURE__ */ createEffectComponent< + typeof BrightnessContrastEffect, + EffectOptions +>(BrightnessContrastEffect) diff --git a/src/effects/ChromaticAberration.tsx b/src/effects/ChromaticAberration.tsx index c768071c..bbbfbfc4 100644 --- a/src/effects/ChromaticAberration.tsx +++ b/src/effects/ChromaticAberration.tsx @@ -1,30 +1,22 @@ import type { ReactThreeFiber } from '@react-three/fiber' import { ChromaticAberrationEffect } from 'postprocessing' import type { Ref } from 'react' -import { useMemo } from 'react' -import { useDispose, useVector2 } from '../util' +import { createEffectComponent, type EffectOptions } from '../createEffectComponent' +// radialModulation/modulationOffset are typed as required by postprocessing's +// own .d.ts, but its JSDoc confirms both are optional with defaults - an +// upstream declaration bug, not a real constraint. export type ChromaticAberrationProps = Omit< - Partial[0]>, - 'offset' + EffectOptions, + 'offset' | 'radialModulation' | 'modulationOffset' > & { - ref?: Ref offset?: ReactThreeFiber.Vector2 + radialModulation?: boolean + modulationOffset?: number + ref?: Ref } -export function ChromaticAberration({ ref, ...props }: ChromaticAberrationProps) { - const offset = useVector2(props, 'offset') - - const effect = useMemo( - () => - new ChromaticAberrationEffect({ - ...props, - offset, - } as ConstructorParameters[0]), - [offset, props] - ) - - useDispose(effect) - - return -} +export const ChromaticAberration = /* @__PURE__ */ createEffectComponent< + typeof ChromaticAberrationEffect, + ChromaticAberrationProps +>(ChromaticAberrationEffect) diff --git a/src/effects/ColorAverage.tsx b/src/effects/ColorAverage.tsx index 5a56292e..fe6a640d 100644 --- a/src/effects/ColorAverage.tsx +++ b/src/effects/ColorAverage.tsx @@ -1,17 +1,4 @@ -import { BlendFunction, ColorAverageEffect } from 'postprocessing' -import type { Ref } from 'react' -import { useMemo } from 'react' -import { useDispose } from '../util' +import { ColorAverageEffect } from 'postprocessing' +import { createEffectComponent } from '../createEffectComponent' -export type ColorAverageProps = { - blendFunction?: BlendFunction - ref?: Ref -} - -export function ColorAverage({ blendFunction = BlendFunction.NORMAL, ref }: ColorAverageProps) { - const effect = useMemo(() => new ColorAverageEffect(blendFunction), [blendFunction]) - - useDispose(effect) - - return -} +export const ColorAverage = /* @__PURE__ */ createEffectComponent(ColorAverageEffect) diff --git a/src/effects/ColorDepth.tsx b/src/effects/ColorDepth.tsx index da7610a0..ce293028 100644 --- a/src/effects/ColorDepth.tsx +++ b/src/effects/ColorDepth.tsx @@ -1,4 +1,25 @@ import { ColorDepthEffect } from 'postprocessing' -import { wrapEffect } from '../wrapEffect' +import type { Ref } from 'react' +import { createEffectComponent, type EffectOptions } from '../createEffectComponent' -export const ColorDepth = /* @__PURE__ */ wrapEffect(ColorDepthEffect) +const ColorDepthImpl = /* @__PURE__ */ createEffectComponent< + typeof ColorDepthEffect, + Omit, 'bits'> & { bitDepth?: number } +>(ColorDepthEffect) + +export type ColorDepthProps = EffectOptions & { + opacity?: number + ref?: Ref +} + +// bits (the constructor's option name) has no live setter of its own in +// postprocessing - only the differently-named bitDepth does (bits is a +// plain, dead field on the instance). Renamed here so it still works as a +// plain prop after the initial mount. +export function ColorDepth({ bits, ...props }: ColorDepthProps) { + // Only set bitDepth when bits is actually provided - r3f's reset-on- + // removal only fires when a key is absent from the new props, not when + // it's present but undefined. + if (bits !== undefined) (props as Record).bitDepth = bits + return +} diff --git a/src/effects/Depth.tsx b/src/effects/Depth.tsx index abebf114..ddc10642 100644 --- a/src/effects/Depth.tsx +++ b/src/effects/Depth.tsx @@ -1,4 +1,6 @@ import { DepthEffect } from 'postprocessing' -import { wrapEffect } from '../wrapEffect' +import { createEffectComponent, type EffectOptions } from '../createEffectComponent' -export const Depth = /* @__PURE__ */ wrapEffect(DepthEffect) +export const Depth = /* @__PURE__ */ createEffectComponent>( + DepthEffect +) diff --git a/src/effects/DepthOfField.tsx b/src/effects/DepthOfField.tsx index 5c9b1fad..ba9aa3ec 100644 --- a/src/effects/DepthOfField.tsx +++ b/src/effects/DepthOfField.tsx @@ -4,7 +4,7 @@ import type { Ref } from 'react' import { use, useMemo } from 'react' import { type DepthPackingStrategies, type Texture, Vector3 } from 'three' import { EffectComposerContext } from '../EffectComposer' -import { useDispose } from '../util' +import { applyPierced, readPierced, useDispose, useLiveDefaults } from '../util' export type DepthOfFieldProps = ConstructorParameters[1] & Partial<{ @@ -19,6 +19,37 @@ export type DepthOfFieldProps = ConstructorParameters blur: number }> +// Only bokehScale, focusDistance/focusRange (via the nested cocMaterial), +// depthTexture (via setDepthTexture) and blendFunction have real setters in +// postprocessing - every resolution option is construction-only. camera +// being a required constructor arg also rules out createEffectComponent +// (needs `new Effect()` to work with zero args). +const LIVE_KEYS = [ + 'blendMode-blendFunction', + 'bokehScale', + 'cocMaterial-focusDistance', + 'cocMaterial-focusRange', + 'depthTexture', +] + +// cocMaterial.depthBuffer/depthPacking are write-only in postprocessing +// (setters with no matching getters) - depthPacking can't be read back at +// all, so a reverted default always re-applies BasicDepthPacking (the same +// value setDepthTexture itself defaults to when packing is omitted). +function get(effect: DepthOfFieldEffect, key: string): unknown { + if (key !== 'depthTexture') return readPierced(effect, key) + const texture = (effect.cocMaterial as unknown as { uniforms: { depthBuffer: { value: unknown } } }).uniforms + .depthBuffer.value + return texture ? { texture } : undefined +} + +function set(effect: DepthOfFieldEffect, key: string, value: unknown): void { + if (key === 'depthTexture') { + const dt = value as { texture?: Texture; packing?: DepthPackingStrategies } | undefined + effect.setDepthTexture(dt?.texture as never, dt?.packing) + } else applyPierced(effect, key, value) +} + export function DepthOfField({ ref, blendFunction, @@ -42,13 +73,9 @@ export function DepthOfField({ const effect = useMemo(() => { const effect = new DepthOfFieldEffect(camera, { - blendFunction, worldFocusDistance, worldFocusRange, - focusDistance, - focusRange, focalLength, - bokehScale, resolutionScale, resolutionX, resolutionY, @@ -57,29 +84,24 @@ export function DepthOfField({ }) // Creating a target enables autofocus, R3F will set via props if (autoFocus) effect.target = new Vector3() - // Depth texture for depth picking with optional packing strategy - if (depthTexture) effect.setDepthTexture(depthTexture.texture, depthTexture.packing as DepthPackingStrategies) // Temporary fix that restores DOF 6.21.3 behavior, everything since then lets shapes leak through the blur - const maskPass = (effect as any).maskPass - maskPass.maskFunction = MaskFunction.MULTIPLY_RGB_SET_ALPHA + effect.maskFunction = MaskFunction.MULTIPLY_RGB_SET_ALPHA return effect - }, [ - camera, - blendFunction, - worldFocusDistance, - worldFocusRange, - focusDistance, - focusRange, - focalLength, - bokehScale, - resolutionScale, - resolutionX, - resolutionY, - width, - height, - autoFocus, - depthTexture, - ]) + }, [camera, worldFocusDistance, worldFocusRange, focalLength, resolutionScale, resolutionX, resolutionY, width, height, autoFocus]) + + useLiveDefaults( + effect, + { + 'blendMode-blendFunction': blendFunction, + bokehScale, + 'cocMaterial-focusDistance': focusDistance, + 'cocMaterial-focusRange': focusRange, + depthTexture, + }, + LIVE_KEYS, + get, + set + ) useDispose(effect) diff --git a/src/effects/DotScreen.tsx b/src/effects/DotScreen.tsx index 8bd72976..b480ecc3 100644 --- a/src/effects/DotScreen.tsx +++ b/src/effects/DotScreen.tsx @@ -1,4 +1,7 @@ import { DotScreenEffect } from 'postprocessing' -import { wrapEffect } from '../wrapEffect' +import { createEffectComponent, type EffectOptions } from '../createEffectComponent' -export const DotScreen = /* @__PURE__ */ wrapEffect(DotScreenEffect) +export const DotScreen = /* @__PURE__ */ createEffectComponent< + typeof DotScreenEffect, + EffectOptions +>(DotScreenEffect) diff --git a/src/effects/FXAA.tsx b/src/effects/FXAA.tsx index 4214767f..1c93ac52 100644 --- a/src/effects/FXAA.tsx +++ b/src/effects/FXAA.tsx @@ -1,4 +1,6 @@ import { FXAAEffect } from 'postprocessing' -import { wrapEffect } from '../wrapEffect' +import { createEffectComponent, type EffectOptions } from '../createEffectComponent' -export const FXAA = /* @__PURE__ */ wrapEffect(FXAAEffect) +export const FXAA = /* @__PURE__ */ createEffectComponent>( + FXAAEffect +) diff --git a/src/effects/Glitch.tsx b/src/effects/Glitch.tsx index 488823c8..3d9befa5 100644 --- a/src/effects/Glitch.tsx +++ b/src/effects/Glitch.tsx @@ -1,37 +1,32 @@ -import { ReactThreeFiber, useThree } from '@react-three/fiber' +import type { ReactThreeFiber } from '@react-three/fiber' import { GlitchEffect, GlitchMode } from 'postprocessing' -import { Ref, useLayoutEffect, useMemo } from 'react' -import { useDispose, useVector2 } from '../util' +import type { Ref } from 'react' +import { useMemo } from 'react' +import { createEffectComponent, type EffectOptions } from '../createEffectComponent' -export type GlitchProps = ConstructorParameters[0] & - Partial<{ - mode: GlitchMode - active: boolean - delay: ReactThreeFiber.Vector2 - duration: ReactThreeFiber.Vector2 - chromaticAberrationOffset: ReactThreeFiber.Vector2 - strength: ReactThreeFiber.Vector2 - ref?: Ref - }> - -export function Glitch({ active = true, ref, ...props }: GlitchProps) { - const invalidate = useThree((state) => state.invalidate) - const delay = useVector2(props, 'delay') - const duration = useVector2(props, 'duration') - const strength = useVector2(props, 'strength') - const chromaticAberrationOffset = useVector2(props, 'chromaticAberrationOffset') - - const effect = useMemo( - () => new GlitchEffect({ ...props, delay, duration, strength, chromaticAberrationOffset }), - [delay, duration, props, strength, chromaticAberrationOffset] - ) +type GlitchOptions = Omit< + EffectOptions, + 'delay' | 'duration' | 'strength' | 'chromaticAberrationOffset' +> & { + delay?: ReactThreeFiber.Vector2 + duration?: ReactThreeFiber.Vector2 + strength?: ReactThreeFiber.Vector2 + chromaticAberrationOffset?: ReactThreeFiber.Vector2 + mode?: GlitchMode +} - useLayoutEffect(() => { - effect.mode = active ? props.mode || GlitchMode.SPORADIC : GlitchMode.DISABLED - invalidate() - }, [active, effect, invalidate, props.mode]) +const GlitchImpl = /* @__PURE__ */ createEffectComponent(GlitchEffect) - useDispose(effect) +export type GlitchProps = GlitchOptions & { + active?: boolean + opacity?: number + ref?: Ref +} - return +// dtSize only seeds the auto-generated perturbation map at construction time +// (skipped entirely once a perturbationMap is provided) - routed through +// args so it still works as a plain prop. +export function Glitch({ active = true, mode = GlitchMode.SPORADIC, dtSize, ...props }: GlitchProps) { + const args = useMemo<[EffectOptions]>(() => [{ dtSize }], [dtSize]) + return } diff --git a/src/effects/GodRays.tsx b/src/effects/GodRays.tsx index b4a3df6b..ae1b9851 100644 --- a/src/effects/GodRays.tsx +++ b/src/effects/GodRays.tsx @@ -1,18 +1,102 @@ +import { useThree } from '@react-three/fiber' import { GodRaysEffect } from 'postprocessing' -import { Ref, RefObject, useContext, useLayoutEffect, useMemo } from 'react' +import { Ref, RefObject, use, useEffect, useLayoutEffect, useMemo } from 'react' import { Mesh, Points } from 'three' import { EffectComposerContext } from '../EffectComposer' -import { resolveRef, useDispose } from '../util' +import { applyPierced, readPierced, resolveRef, useDispose, useLiveDefaults } from '../util' type GodRaysProps = ConstructorParameters[2] & { sun: Mesh | Points | RefObject ref?: Ref } -export function GodRays({ ref, ...props }: GodRaysProps) { - const { camera } = useContext(EffectComposerContext) - const effect = useMemo(() => new GodRaysEffect(camera, resolveRef(props.sun), props), [camera, props]) - useLayoutEffect(() => void (effect.lightSource = resolveRef(props.sun)), [effect, props.sun]) +// GodRaysMaterial (godRaysMaterial) is where density/decay/weight/exposure +// actually live - clampMax maps to its differently-named maxIntensity. +// resolutionScale/resolutionX/resolutionY have no setter at all in +// postprocessing - construction-only. camera+sun being required constructor +// args also rule out createEffectComponent (needs `new Effect()` to work +// with zero args). +const LIVE_KEYS = [ + 'blendMode-blendFunction', + 'godRaysMaterial-density', + 'godRaysMaterial-decay', + 'godRaysMaterial-weight', + 'godRaysMaterial-exposure', + 'clampMax', + 'blur', + 'kernelSize', + 'samples', + 'width', + 'height', +] + +function get(effect: GodRaysEffect, key: string): unknown { + return key === 'clampMax' ? effect.godRaysMaterial.maxIntensity : readPierced(effect, key) +} + +function set(effect: GodRaysEffect, key: string, value: unknown): void { + if (key === 'clampMax') effect.godRaysMaterial.maxIntensity = value as number + else applyPierced(effect, key, value) +} + +export function GodRays({ + sun, + blendFunction, + density, + decay, + weight, + exposure, + clampMax, + blur, + kernelSize, + samples, + width, + height, + resolutionScale, + resolutionX, + resolutionY, + ref, +}: GodRaysProps) { + const { camera, autoClear } = use(EffectComposerContext) + const invalidate = useThree((state) => state.invalidate) + + const effect = useMemo( + () => new GodRaysEffect(camera, resolveRef(sun), { resolutionScale, resolutionX, resolutionY }), + [camera, resolutionScale, resolutionX, resolutionY] + ) + + useEffect(() => { + if (autoClear !== false) { + console.warn( + 'GodRays renders an internal extra pass that needs - without it, occlusion by other objects will look wrong.' + ) + } + }, [autoClear]) + + useLayoutEffect(() => { + effect.lightSource = resolveRef(sun) + invalidate() + }, [effect, sun, invalidate]) + + useLiveDefaults( + effect, + { + 'blendMode-blendFunction': blendFunction, + 'godRaysMaterial-density': density, + 'godRaysMaterial-decay': decay, + 'godRaysMaterial-weight': weight, + 'godRaysMaterial-exposure': exposure, + clampMax, + blur, + kernelSize, + samples, + width, + height, + }, + LIVE_KEYS, + get, + set + ) useDispose(effect) diff --git a/src/effects/Grid.tsx b/src/effects/Grid.tsx index 639e22b0..f818ce56 100644 --- a/src/effects/Grid.tsx +++ b/src/effects/Grid.tsx @@ -1,28 +1,27 @@ import { useThree } from '@react-three/fiber' import { GridEffect } from 'postprocessing' -import { Ref, useLayoutEffect, useMemo } from 'react' -import { useDispose } from '../util' +import { type Ref, useImperativeHandle, useLayoutEffect, useRef } from 'react' +import { createEffectComponent, type EffectOptions } from '../createEffectComponent' -type GridProps = ConstructorParameters[0] & - Partial<{ - size: { - width: number - height: number - } - ref: Ref - }> +const GridImpl = /* @__PURE__ */ createEffectComponent>(GridEffect) + +export type GridProps = EffectOptions & { + size?: { width: number; height: number } + opacity?: number + ref?: Ref +} export function Grid({ size, ref, ...props }: GridProps) { const invalidate = useThree((state) => state.invalidate) - - const effect = useMemo(() => new GridEffect(props), [props]) + const localRef = useRef(null) + useImperativeHandle(ref, () => localRef.current!, []) useLayoutEffect(() => { - if (size) effect.setSize(size.width, size.height) - invalidate() - }, [effect, size, invalidate]) - - useDispose(effect) + if (size) { + localRef.current?.setSize(size.width, size.height) + invalidate() + } + }, [size, invalidate]) - return + return } diff --git a/src/effects/HueSaturation.tsx b/src/effects/HueSaturation.tsx index 7a27c193..d791208e 100644 --- a/src/effects/HueSaturation.tsx +++ b/src/effects/HueSaturation.tsx @@ -1,4 +1,7 @@ import { HueSaturationEffect } from 'postprocessing' -import { wrapEffect } from '../wrapEffect' +import { createEffectComponent, type EffectOptions } from '../createEffectComponent' -export const HueSaturation = /* @__PURE__ */ wrapEffect(HueSaturationEffect) +export const HueSaturation = /* @__PURE__ */ createEffectComponent< + typeof HueSaturationEffect, + EffectOptions +>(HueSaturationEffect) diff --git a/src/effects/LUT.tsx b/src/effects/LUT.tsx index f1e277c4..5db77e36 100644 --- a/src/effects/LUT.tsx +++ b/src/effects/LUT.tsx @@ -1,8 +1,7 @@ -import { useThree } from '@react-three/fiber' import { BlendFunction, LUT3DEffect } from 'postprocessing' -import { Ref, useLayoutEffect, useMemo } from 'react' +import { Ref, useMemo } from 'react' import type { Texture } from 'three' -import { useDispose } from '../util' +import { useDispose, useLiveDefaults } from '../util' export type LUTProps = { lut: Texture @@ -11,16 +10,15 @@ export type LUTProps = { ref?: Ref } -export function LUT({ lut, tetrahedralInterpolation, ref, ...props }: LUTProps) { - const effect = useMemo(() => new LUT3DEffect(lut, props), [lut, props]) - const invalidate = useThree((state) => state.invalidate) +const LIVE_KEYS = ['blendMode-blendFunction', 'lut', 'tetrahedralInterpolation'] - useLayoutEffect(() => { - if (tetrahedralInterpolation) effect.tetrahedralInterpolation = tetrahedralInterpolation - if (lut) effect.lut = lut - invalidate() - }, [effect, invalidate, lut, tetrahedralInterpolation]) +// lut is LUT3DEffect's required constructor arg (no default) - only used +// for the initial instance, later changes go through its own live setter +// (via useLiveDefaults below) instead of reconstructing. +export function LUT({ lut, blendFunction, tetrahedralInterpolation, ref }: LUTProps) { + const effect = useMemo(() => new LUT3DEffect(lut), []) + useLiveDefaults(effect, { 'blendMode-blendFunction': blendFunction, lut, tetrahedralInterpolation }, LIVE_KEYS) useDispose(effect) return diff --git a/src/effects/LensFlare.tsx b/src/effects/LensFlare.tsx index 4cb9d3c1..40092813 100644 --- a/src/effects/LensFlare.tsx +++ b/src/effects/LensFlare.tsx @@ -1,14 +1,15 @@ // Created by Anderson Mancini 2023 // From https://github.com/ektogamat/R3F-Ultimate-Lens-Flare -import { useFrame, useThree } from '@react-three/fiber' +import { useFrame, useThree, type ReactThreeFiber } from '@react-three/fiber' import { easing } from 'maath' import { BlendFunction, Effect } from 'postprocessing' -import { useContext, useEffect, useRef, useState } from 'react' +import { useContext, useEffect, useRef, useState, type Ref } from 'react' import { Color, Mesh, Texture, Uniform, Vector2, Vector3 } from 'three' +import { createEffectComponent } from '../createEffectComponent' import { EffectComposerContext } from '../EffectComposer' -import { wrapEffect } from '../wrapEffect' +import { useMergeRefs, useVector3 } from '../util' const LensFlareShader = { fragmentShader: /* glsl */ ` @@ -441,26 +442,26 @@ type LensFlareEffectOptions = { export class LensFlareEffect extends Effect { constructor({ - blendFunction, - enabled, - glareSize, - lensPosition, - screenRes, - starPoints, - flareSize, - flareSpeed, - flareShape, - animated, - anamorphic, - colorGain, - lensDirtTexture, - haloScale, - secondaryGhosts, - aditionalStreaks, - ghostScale, - opacity, - starBurst, - }: LensFlareEffectOptions) { + blendFunction = BlendFunction.NORMAL, + enabled = true, + glareSize = 0.2, + lensPosition = new Vector3(-25, 6, -60), + screenRes = new Vector2(0, 0), + starPoints = 6, + flareSize = 0.01, + flareSpeed = 0.01, + flareShape = 0.01, + animated = true, + anamorphic = false, + colorGain = new Color(20, 20, 20), + lensDirtTexture = null, + haloScale = 0.5, + secondaryGhosts = true, + aditionalStreaks = true, + ghostScale = 0.0, + opacity = 1.0, + starBurst = false, + }: Partial = {}) { super('LensFlareEffect', LensFlareShader.fragmentShader, { blendFunction, uniforms: new Map([ @@ -493,40 +494,191 @@ export class LensFlareEffect extends Effect { time.value += deltaTime } } + + private u(name: string): T { + return this.uniforms.get(name)!.value + } + + private setU(name: string, value: unknown): void { + this.uniforms.get(name)!.value = value + } + + get enabled(): boolean { + return this.u('enabled') + } + set enabled(value: boolean) { + this.setU('enabled', value) + } + + get glareSize(): number { + return this.u('glareSize') + } + set glareSize(value: number) { + this.setU('glareSize', value) + } + + get lensPosition(): Vector3 { + return this.u('lensPosition') + } + set lensPosition(value: Vector3) { + this.setU('lensPosition', value) + } + + get screenRes(): Vector2 { + return this.u('screenRes') + } + set screenRes(value: Vector2) { + this.setU('screenRes', value) + } + + get starPoints(): number { + return this.u('starPoints') + } + set starPoints(value: number) { + this.setU('starPoints', value) + } + + get flareSize(): number { + return this.u('flareSize') + } + set flareSize(value: number) { + this.setU('flareSize', value) + } + + get flareSpeed(): number { + return this.u('flareSpeed') + } + set flareSpeed(value: number) { + this.setU('flareSpeed', value) + } + + get flareShape(): number { + return this.u('flareShape') + } + set flareShape(value: number) { + this.setU('flareShape', value) + } + + get animated(): boolean { + return this.u('animated') + } + set animated(value: boolean) { + this.setU('animated', value) + } + + get anamorphic(): boolean { + return this.u('anamorphic') + } + set anamorphic(value: boolean) { + this.setU('anamorphic', value) + } + + get colorGain(): Color { + return this.u('colorGain') + } + set colorGain(value: Color) { + this.setU('colorGain', value) + } + + get lensDirtTexture(): Texture | null { + return this.u('lensDirtTexture') + } + set lensDirtTexture(value: Texture | null) { + this.setU('lensDirtTexture', value) + } + + get haloScale(): number { + return this.u('haloScale') + } + set haloScale(value: number) { + this.setU('haloScale', value) + } + + get secondaryGhosts(): boolean { + return this.u('secondaryGhosts') + } + set secondaryGhosts(value: boolean) { + this.setU('secondaryGhosts', value) + } + + get aditionalStreaks(): boolean { + return this.u('aditionalStreaks') + } + set aditionalStreaks(value: boolean) { + this.setU('aditionalStreaks', value) + } + + get ghostScale(): number { + return this.u('ghostScale') + } + set ghostScale(value: number) { + this.setU('ghostScale', value) + } + + get starBurst(): boolean { + return this.u('starBurst') + } + set starBurst(value: boolean) { + this.setU('starBurst', value) + } + + get opacity(): number { + return this.u('opacity') + } + set opacity(value: number) { + this.setU('opacity', value) + } } -type LensFlareProps = { +type LensFlareProps = Omit, 'lensPosition'> & { /** Position of the effect */ - lensPosition?: Vector3 + lensPosition?: ReactThreeFiber.Vector3 /** The time that it takes to fade the occlusion */ smoothTime?: number -} & Partial - -const LensFlareWrapped = /* @__PURE__ */ wrapEffect(LensFlareEffect) - -export const LensFlare = ({ - smoothTime = 0.07, - // - blendFunction = BlendFunction.NORMAL, - enabled = true, - glareSize = 0.2, - lensPosition = new Vector3(-25, 6, -60), - screenRes = new Vector2(0, 0), - starPoints = 6, - flareSize = 0.01, - flareSpeed = 0.01, - flareShape = 0.01, - animated = true, - anamorphic = false, - colorGain = new Color(20, 20, 20), - lensDirtTexture = null, - haloScale = 0.5, - secondaryGhosts = true, - aditionalStreaks = true, - ghostScale = 0.0, - opacity = 1.0, - starBurst = false, -}: LensFlareProps) => { + ref?: Ref +} + +const LensFlareWrapped = /* @__PURE__ */ createEffectComponent< + typeof LensFlareEffect, + Partial & { ref?: Ref } +>(LensFlareEffect) + +// Stable identity so a parent re-render (e.g. a Leva control) doesn't hand +// r3f a brand-new Vector2 every time - r3f would then `.copy()` it onto the +// uniform in place, stomping the viewport-driven value the effect below +// maintains and leaving screenRes at (0, 0) until the next resize. Never +// mutated (only ever copied *from*), so it's safe to share across instances. +const DEFAULT_SCREEN_RES = /* @__PURE__ */ new Vector2(0, 0) + +// Same reasoning as DEFAULT_SCREEN_RES above - shared, read-only default. +const DEFAULT_LENS_POSITION = /* @__PURE__ */ new Vector3(-25, 6, -60) + +export const LensFlare = (props: LensFlareProps) => { + const { + ref: forwardedRef, + smoothTime = 0.07, + blendFunction = BlendFunction.NORMAL, + enabled = true, + glareSize = 0.2, + screenRes, + starPoints = 6, + flareSize = 0.01, + flareSpeed = 0.01, + flareShape = 0.01, + animated = true, + anamorphic = false, + colorGain = new Color(20, 20, 20), + lensDirtTexture = null, + haloScale = 0.5, + secondaryGhosts = true, + aditionalStreaks = true, + ghostScale = 0.0, + opacity = 1.0, + starBurst = false, + } = props + + const lensPosition = useVector3({ lensPosition: props.lensPosition ?? DEFAULT_LENS_POSITION }, 'lensPosition') + const viewport = useThree(({ viewport }) => viewport) const raycaster = useThree(({ raycaster }) => raycaster) const { scene, camera } = useContext(EffectComposerContext) @@ -534,6 +686,7 @@ export const LensFlare = ({ const [projectedPosition] = useState(() => new Vector3()) const ref = useRef(null) + const setRef = useMergeRefs(ref, forwardedRef) useFrame((_, delta) => { if (!ref?.current) return @@ -541,7 +694,7 @@ export const LensFlare = ({ const uOpacity = ref.current.uniforms.get('opacity') if (!uLensPosition || !uOpacity) return - let target = 1 + let target = 0 projectedPosition.copy(lensPosition).project(camera) if (projectedPosition.z > 1) return @@ -555,6 +708,7 @@ export const LensFlare = ({ const intersects = raycaster.intersectObjects(scene.children, true) const { object } = intersects[0] || {} if (object) { + target = 1 if (object.userData?.lensflare === 'no-occlusion') { target = 0 } else if (object instanceof Mesh) { @@ -586,12 +740,12 @@ export const LensFlare = ({ return ( >( + NoiseEffect +) + +export type NoiseProps = EffectOptions & { + opacity?: number + ref?: Ref +} + +export function Noise({ blendFunction = BlendFunction.COLOR_DODGE, ...props }: NoiseProps) { + return +} diff --git a/src/effects/Outline.tsx b/src/effects/Outline.tsx index 8f8e99a2..6ccd41f5 100644 --- a/src/effects/Outline.tsx +++ b/src/effects/Outline.tsx @@ -1,83 +1,82 @@ import { OutlineEffect } from 'postprocessing' -import { Ref, RefObject, use, useMemo } from 'react' -import { Object3D } from 'three' +import { Ref, RefObject, use, useEffect, useMemo } from 'react' +import { Color, Object3D, type ColorRepresentation } from 'three' import { EffectComposerContext } from '../EffectComposer' -import { EMPTY_ARRAY, useDispose, useSelectionSync } from '../util' +import { applyPierced, EMPTY_ARRAY, readPierced, useDispose, useLiveDefaults, useSelectionSync } from '../util' type ObjectRef = RefObject -export type OutlineProps = ConstructorParameters[2] & +type OutlineEffectOptions = NonNullable[2]> + +export type OutlineProps = Omit & Partial<{ selection: Object3D | Object3D[] | ObjectRef | ObjectRef[] selectionLayer: number + visibleEdgeColor: ColorRepresentation + hiddenEdgeColor: ColorRepresentation ref?: Ref }> +// Every OutlineEffect option that has a real setter (verified against +// postprocessing's source) - resolutionScale/resolutionX/resolutionY are +// the only ones without one, since they only feed the internal blur pass +// at construction time. scene/camera are required constructor args, so +// OutlineEffect can't use createEffectComponent (needs `new Effect()` to +// work with zero args) - built by hand instead. +const LIVE_KEYS = [ + 'patternTexture', + 'patternScale', + 'edgeStrength', + 'pulseSpeed', + 'visibleEdgeColor', + 'hiddenEdgeColor', + 'multisampling', + 'width', + 'height', + 'kernelSize', + 'blur', + 'xRay', + 'dithering', + 'blendMode-blendFunction', +] + +// The setter stores whatever it's given as-is, unlike the constructor - +// wrap in a Color here too, or a raw hex/string breaks the shader uniform. +function set(effect: OutlineEffect, key: string, value: unknown): void { + if (key === 'visibleEdgeColor' || key === 'hiddenEdgeColor') applyPierced(effect, key, new Color(value as never)) + else applyPierced(effect, key, value) +} + export function Outline({ selection = EMPTY_ARRAY, selectionLayer = 10, blendFunction, - patternTexture, - patternScale, - edgeStrength, - pulseSpeed, - visibleEdgeColor, - hiddenEdgeColor, - multisampling, resolutionScale, resolutionX, resolutionY, - width, - height, - kernelSize, - blur, - xRay, ref, + ...liveProps }: OutlineProps) { - const { scene, camera } = use(EffectComposerContext) + const { scene, camera, autoClear } = use(EffectComposerContext) const effect = useMemo( - () => - new OutlineEffect(scene, camera, { - blendFunction, - patternTexture, - patternScale, - edgeStrength, - pulseSpeed, - visibleEdgeColor, - hiddenEdgeColor, - multisampling, - resolutionScale, - resolutionX, - resolutionY, - width, - height, - kernelSize, - blur, - xRay, - }), - [ - blendFunction, - patternTexture, - patternScale, - edgeStrength, - pulseSpeed, - visibleEdgeColor, - hiddenEdgeColor, - multisampling, - resolutionScale, - resolutionX, - resolutionY, - width, - height, - kernelSize, - blur, - xRay, - camera, - scene, - ] + () => new OutlineEffect(scene, camera, { resolutionScale, resolutionX, resolutionY }), + [scene, camera, resolutionScale, resolutionX, resolutionY] ) + useEffect(() => { + if (autoClear !== false) { + console.warn('Outline requires to render correctly.') + } + }, [autoClear]) + + useLiveDefaults( + effect, + { ...liveProps, 'blendMode-blendFunction': blendFunction } as Record, + LIVE_KEYS, + readPierced, + set + ) useSelectionSync(effect, selection, selectionLayer) useDispose(effect) diff --git a/src/effects/Pixelation.tsx b/src/effects/Pixelation.tsx index 66ad13bf..0f392057 100644 --- a/src/effects/Pixelation.tsx +++ b/src/effects/Pixelation.tsx @@ -1,6 +1,14 @@ import { PixelationEffect } from 'postprocessing' -import { Ref, useMemo } from 'react' -import { useDispose } from '../util' +import type { Ref } from 'react' +import { createEffectComponent } from '../createEffectComponent' + +// PixelationEffect's sole constructor arg is a bare number, not an options +// object - granularity is a real live setter though, so it's just a normal +// prop; only the curated default (5, vs the class's own default of 30) +// needs a thin wrapper. +const PixelationImpl = /* @__PURE__ */ createEffectComponent( + PixelationEffect +) export type PixelationProps = { granularity?: number @@ -8,10 +16,5 @@ export type PixelationProps = { } export function Pixelation({ granularity = 5, ref }: PixelationProps) { - /** Because GlitchEffect granularity is not an object but a number, we have to define a custom prop "granularity" */ - const effect = useMemo(() => new PixelationEffect(granularity), [granularity]) - - useDispose(effect) - - return + return } diff --git a/src/effects/Ramp.tsx b/src/effects/Ramp.tsx index e2dab703..140b0ff6 100644 --- a/src/effects/Ramp.tsx +++ b/src/effects/Ramp.tsx @@ -1,6 +1,7 @@ -import { Effect } from 'postprocessing' +import { BlendFunction, Effect } from 'postprocessing' +import type { Ref } from 'react' import { Uniform } from 'three' -import { wrapEffect } from '../wrapEffect' +import { createEffectComponent } from '../createEffectComponent' const RampShader = { fragmentShader: /* glsl */ ` @@ -72,6 +73,9 @@ export enum RampType { MirroredLinear, } +type RampTuple2 = [number, number] +type RampTuple4 = [number, number, number, number] + export class RampEffect extends Effect { constructor({ /** @@ -83,25 +87,25 @@ export class RampEffect extends Effect { * * Ranges from `[0 - 1]` as `[x, y]`. Default is `[0.5, 0.5]`. */ - rampStart = [0.5, 0.5], + rampStart = [0.5, 0.5] as RampTuple2, /** * Ending point of the ramp gradient in normalized coordinates. * * Ranges from `[0 - 1]` as `[x, y]`. Default is `[1, 1]` */ - rampEnd = [1, 1], + rampEnd = [1, 1] as RampTuple2, /** * Color at the starting point of the gradient. * * Default is black: `[0, 0, 0, 1]` */ - startColor = [0, 0, 0, 1], + startColor = [0, 0, 0, 1] as RampTuple4, /** * Color at the ending point of the gradient. * * Default is white: `[1, 1, 1, 1]` */ - endColor = [1, 1, 1, 1], + endColor = [1, 1, 1, 1] as RampTuple4, /** * Bias for the interpolation curve when both bias and gain are 0.5. * @@ -145,6 +149,91 @@ export class RampEffect extends Effect { ]), }) } + + private u(name: string): T { + return this.uniforms.get(name)!.value + } + + private setU(name: string, value: unknown): void { + this.uniforms.get(name)!.value = value + } + + get rampType(): RampType { + return this.u('rampType') + } + set rampType(value: RampType) { + this.setU('rampType', value) + } + + get rampStart(): RampTuple2 { + return this.u('rampStart') + } + set rampStart(value: RampTuple2) { + this.setU('rampStart', value) + } + + get rampEnd(): RampTuple2 { + return this.u('rampEnd') + } + set rampEnd(value: RampTuple2) { + this.setU('rampEnd', value) + } + + get startColor(): RampTuple4 { + return this.u('startColor') + } + set startColor(value: RampTuple4) { + this.setU('startColor', value) + } + + get endColor(): RampTuple4 { + return this.u('endColor') + } + set endColor(value: RampTuple4) { + this.setU('endColor', value) + } + + get rampBias(): number { + return this.u('rampBias') + } + set rampBias(value: number) { + this.setU('rampBias', value) + } + + get rampGain(): number { + return this.u('rampGain') + } + set rampGain(value: number) { + this.setU('rampGain', value) + } + + get rampMask(): boolean { + return this.u('rampMask') + } + set rampMask(value: boolean) { + this.setU('rampMask', value) + } + + get rampInvert(): boolean { + return this.u('rampInvert') + } + set rampInvert(value: boolean) { + this.setU('rampInvert', value) + } +} + +export type RampProps = { + blendFunction?: BlendFunction + rampType?: RampType + rampStart?: RampTuple2 + rampEnd?: RampTuple2 + startColor?: RampTuple4 + endColor?: RampTuple4 + rampBias?: number + rampGain?: number + rampMask?: boolean + rampInvert?: boolean + ref?: Ref } -export const Ramp = /* @__PURE__ */ wrapEffect(RampEffect) +export const Ramp = /* @__PURE__ */ createEffectComponent(RampEffect) diff --git a/src/effects/SMAA.tsx b/src/effects/SMAA.tsx index 9e41b1b9..6eab5e59 100644 --- a/src/effects/SMAA.tsx +++ b/src/effects/SMAA.tsx @@ -1,4 +1,20 @@ import { SMAAEffect } from 'postprocessing' -import { wrapEffect } from '../wrapEffect' +import type { Ref } from 'react' +import { useMemo } from 'react' +import { createEffectComponent, type EffectOptions } from '../createEffectComponent' -export const SMAA = /* @__PURE__ */ wrapEffect(SMAAEffect) +type SMAAOptions = EffectOptions + +const SMAAImpl = /* @__PURE__ */ createEffectComponent(SMAAEffect) + +export type SMAAProps = SMAAOptions & { opacity?: number; ref?: Ref } + +// preset/edgeDetectionMode/predicationMode have no live setter in +// postprocessing - routed through args so they still work as plain props. +export function SMAA({ preset, edgeDetectionMode, predicationMode, ...liveProps }: SMAAProps) { + const args = useMemo<[SMAAOptions]>( + () => [{ preset, edgeDetectionMode, predicationMode }], + [preset, edgeDetectionMode, predicationMode] + ) + return +} diff --git a/src/effects/SSAO.tsx b/src/effects/SSAO.tsx index 2d5fd723..e1db76b8 100644 --- a/src/effects/SSAO.tsx +++ b/src/effects/SSAO.tsx @@ -1,13 +1,81 @@ import { BlendFunction, SSAOEffect } from 'postprocessing' -import { Ref, useContext, useMemo } from 'react' +import { Ref, use, useMemo } from 'react' import { EffectComposerContext } from '../EffectComposer' -import { useDispose } from '../util' +import { applyPierced, readPierced, useDispose, useLiveDefaults } from '../util' // first two args are camera and texture type SSAOProps = ConstructorParameters[2] & { ref?: Ref } -export function SSAO({ ref, ...props }: SSAOProps) { - const { camera, normalPass, downSamplingPass, resolutionScale } = useContext(EffectComposerContext) +// Only resolutionScale/resolutionX/resolutionY/width/height and +// normalDepthBuffer have no live setter in postprocessing - everything else +// either has a real accessor directly on SSAOEffect, or on the nested +// ssaoMaterial (rangeThreshold/rangeFalloff are the constructor's names for +// what ssaoMaterial exposes as proximityThreshold/proximityFalloff). +// camera+normalBuffer being required constructor args also rule out +// createEffectComponent (needs `new Effect()` to work with zero args). +const LIVE_KEYS = [ + 'blendMode-blendFunction', + 'normalBuffer', + 'samples', + 'rings', + 'radius', + 'depthAwareUpsampling', + 'color', + 'luminanceInfluence', + 'intensity', + 'ssaoMaterial-bias', + 'ssaoMaterial-fade', + 'ssaoMaterial-minRadiusScale', + 'ssaoMaterial-distanceThreshold', + 'ssaoMaterial-distanceFalloff', + 'ssaoMaterial-worldDistanceThreshold', + 'ssaoMaterial-worldDistanceFalloff', + 'rangeThreshold', + 'rangeFalloff', + 'ssaoMaterial-worldProximityThreshold', + 'ssaoMaterial-worldProximityFalloff', +] + +function get(effect: SSAOEffect, key: string): unknown { + if (key === 'rangeThreshold') return effect.ssaoMaterial.proximityThreshold + if (key === 'rangeFalloff') return effect.ssaoMaterial.proximityFalloff + return readPierced(effect, key) +} + +function set(effect: SSAOEffect, key: string, value: unknown): void { + if (key === 'rangeThreshold') effect.ssaoMaterial.proximityThreshold = value as number + else if (key === 'rangeFalloff') effect.ssaoMaterial.proximityFalloff = value as number + else applyPierced(effect, key, value) +} + +export function SSAO({ + blendFunction = BlendFunction.MULTIPLY, + samples = 30, + rings = 4, + distanceThreshold = 1.0, + distanceFalloff = 0.0, + rangeThreshold = 0.5, + rangeFalloff = 0.1, + luminanceInfluence = 0.9, + radius = 20, + bias = 0.5, + intensity = 1.0, + color, + worldDistanceThreshold, + worldDistanceFalloff, + worldProximityThreshold, + worldProximityFalloff, + minRadiusScale, + fade, + depthAwareUpsampling = true, + resolutionScale, + resolutionX, + resolutionY, + width, + height, + ref, +}: SSAOProps) { + const { camera, normalPass, downSamplingPass, resolutionScale: composerResolutionScale } = use(EffectComposerContext) const effect = useMemo(() => { if (normalPass === null && downSamplingPass === null) { @@ -16,29 +84,69 @@ export function SSAO({ ref, ...props }: SSAOProps) { } return new SSAOEffect(camera, normalPass && !downSamplingPass ? (normalPass as any).texture : null, { - blendFunction: BlendFunction.MULTIPLY, - samples: 30, - rings: 4, - distanceThreshold: 1.0, - distanceFalloff: 0.0, - rangeThreshold: 0.5, - rangeFalloff: 0.1, - luminanceInfluence: 0.9, - radius: 20, - bias: 0.5, - intensity: 1.0, - color: undefined, + blendFunction, + samples, + rings, + distanceThreshold, + distanceFalloff, + rangeThreshold, + rangeFalloff, + luminanceInfluence, + radius, + bias, + intensity, // @ts-ignore normalDepthBuffer: downSamplingPass ? downSamplingPass.texture : null, - resolutionScale: resolutionScale ?? 1, - depthAwareUpsampling: true, - ...props, + resolutionScale: resolutionScale ?? composerResolutionScale ?? 1, + resolutionX, + resolutionY, + width, + height, + depthAwareUpsampling, }) - // NOTE: `props` is an unstable reference, so we can't memoize it + // color/worldDistanceThreshold/worldDistanceFalloff/worldProximityThreshold/ + // worldProximityFalloff/minRadiusScale/fade are deliberately left out here + // even though they're valid constructor options: they have no JS-level + // default in this component's own signature, so useLiveDefaults' first + // snapshot must see SSAOEffect's own real default for them, not whatever + // value happened to be passed on the mounting render - otherwise removing + // the prop later "resets" to that first-render value instead of the + // effect's true default. They're still applied immediately below, live. + // + // Only the genuinely construction-only options belong here - everything + // else is applied live below via useLiveDefaults instead. // eslint-disable-next-line react-hooks/exhaustive-deps - }, [camera, downSamplingPass, normalPass, resolutionScale]) + }, [camera, downSamplingPass, normalPass, resolutionScale, composerResolutionScale, resolutionX, resolutionY, width, height]) + + useLiveDefaults( + effect instanceof SSAOEffect ? effect : null, + { + 'blendMode-blendFunction': blendFunction, + samples, + rings, + radius, + depthAwareUpsampling, + color, + luminanceInfluence, + intensity, + 'ssaoMaterial-bias': bias, + 'ssaoMaterial-fade': fade, + 'ssaoMaterial-minRadiusScale': minRadiusScale, + 'ssaoMaterial-distanceThreshold': distanceThreshold, + 'ssaoMaterial-distanceFalloff': distanceFalloff, + 'ssaoMaterial-worldDistanceThreshold': worldDistanceThreshold, + 'ssaoMaterial-worldDistanceFalloff': worldDistanceFalloff, + rangeThreshold, + rangeFalloff, + 'ssaoMaterial-worldProximityThreshold': worldProximityThreshold, + 'ssaoMaterial-worldProximityFalloff': worldProximityFalloff, + }, + LIVE_KEYS, + get, + set + ) - useDispose(effect) + useDispose(effect as SSAOEffect) return } diff --git a/src/effects/ScanlineEffect.tsx b/src/effects/ScanlineEffect.tsx index ed34430a..6fe48b64 100644 --- a/src/effects/ScanlineEffect.tsx +++ b/src/effects/ScanlineEffect.tsx @@ -1,7 +1,7 @@ -import { BlendFunction, ScanlineEffect } from 'postprocessing' -import { wrapEffect } from '../wrapEffect' +import { ScanlineEffect } from 'postprocessing' +import { createEffectComponent, type EffectOptions } from '../createEffectComponent' -export const Scanline = /* @__PURE__ */ wrapEffect(ScanlineEffect, { - blendFunction: BlendFunction.OVERLAY, - density: 1.25, -}) +export const Scanline = /* @__PURE__ */ createEffectComponent< + typeof ScanlineEffect, + EffectOptions +>(ScanlineEffect) diff --git a/src/effects/SelectiveBloom.tsx b/src/effects/SelectiveBloom.tsx index 7007dddc..fc080af5 100644 --- a/src/effects/SelectiveBloom.tsx +++ b/src/effects/SelectiveBloom.tsx @@ -4,7 +4,7 @@ import { BlendFunction, SelectiveBloomEffect } from 'postprocessing' import { Ref, RefObject, use, useEffect, useMemo } from 'react' import { Object3D } from 'three' import { EffectComposerContext } from '../EffectComposer' -import { EMPTY_ARRAY, resolveRef, useDispose, useSelectionSync } from '../util' +import { EMPTY_ARRAY, resolveRef, useDispose, useLiveDefaults, useSelectionSync } from '../util' type ObjectRef = RefObject @@ -21,67 +21,49 @@ export type SelectiveBloomProps = BloomEffectOptions & const addLight = (light: Object3D, effect: SelectiveBloomEffect) => light.layers.enable(effect.selection.layer) const removeLight = (light: Object3D, effect: SelectiveBloomEffect) => light.layers.disable(effect.selection.layer) +// BloomEffect (which SelectiveBloomEffect extends) only exposes real +// setters for these - luminanceThreshold/luminanceSmoothing/mipmapBlur/ +// radius/levels/resolution* are construction-only in postprocessing itself. +// scene/camera being required constructor args also rules out +// createEffectComponent (needs `new Effect()` to work with zero args). +const LIVE_KEYS = ['width', 'height', 'kernelSize', 'intensity', 'inverted', 'ignoreBackground'] + export function SelectiveBloom({ selection = EMPTY_ARRAY, selectionLayer = 10, lights = EMPTY_ARRAY, - inverted = false, - ignoreBackground = false, luminanceThreshold, luminanceSmoothing, mipmapBlur, - intensity, radius, levels, - kernelSize, resolutionScale, - width, - height, resolutionX, resolutionY, ref, + ...liveProps }: SelectiveBloomProps) { const { scene, camera } = use(EffectComposerContext) const invalidate = useThree((state) => state.invalidate) - const effect = useMemo(() => { - const instance = new SelectiveBloomEffect(scene, camera, { - blendFunction: BlendFunction.ADD, - luminanceThreshold, - luminanceSmoothing, - mipmapBlur, - intensity, - radius, - levels, - kernelSize, - resolutionScale, - width, - height, - resolutionX, - resolutionY, - }) - instance.inverted = inverted - instance.ignoreBackground = ignoreBackground - return instance - }, [ - scene, - camera, - luminanceThreshold, - luminanceSmoothing, - mipmapBlur, - intensity, - radius, - levels, - kernelSize, - resolutionScale, - width, - height, - resolutionX, - resolutionY, - inverted, - ignoreBackground, - ]) + const effect = useMemo( + () => + new SelectiveBloomEffect(scene, camera, { + blendFunction: BlendFunction.ADD, + luminanceThreshold, + luminanceSmoothing, + mipmapBlur, + radius, + levels, + resolutionScale, + resolutionX, + resolutionY, + }), + [scene, camera, luminanceThreshold, luminanceSmoothing, mipmapBlur, radius, levels, resolutionScale, resolutionX, resolutionY] + ) + + useLiveDefaults(effect, liveProps as Record, LIVE_KEYS) // Must run before the lights effect below: addLight/removeLight read // effect.selection.layer live, so it needs to already reflect the diff --git a/src/effects/Sepia.tsx b/src/effects/Sepia.tsx index 8142b2bd..891a96c4 100644 --- a/src/effects/Sepia.tsx +++ b/src/effects/Sepia.tsx @@ -1,4 +1,6 @@ import { SepiaEffect } from 'postprocessing' -import { wrapEffect } from '../wrapEffect' +import { createEffectComponent, type EffectOptions } from '../createEffectComponent' -export const Sepia = /* @__PURE__ */ wrapEffect(SepiaEffect) +export const Sepia = /* @__PURE__ */ createEffectComponent>( + SepiaEffect +) diff --git a/src/effects/ShockWave.tsx b/src/effects/ShockWave.tsx index 10da37dc..da292123 100644 --- a/src/effects/ShockWave.tsx +++ b/src/effects/ShockWave.tsx @@ -1,4 +1,32 @@ +import type { ReactThreeFiber } from '@react-three/fiber' import { ShockWaveEffect } from 'postprocessing' -import { wrapEffect } from '../wrapEffect' +import { Ref, use, useMemo } from 'react' +import { EffectComposerContext } from '../EffectComposer' +import { useDispose, useLiveDefaults, useVector3 } from '../util' -export const ShockWave = /* @__PURE__ */ wrapEffect(ShockWaveEffect) +export type ShockWaveProps = { + position?: ReactThreeFiber.Vector3 + speed?: number + maxRadius?: number + waveSize?: number + amplitude?: number + ref?: Ref +} + +const LIVE_KEYS = ['position', 'speed', 'maxRadius', 'waveSize', 'amplitude'] + +// ShockWaveEffect's constructor is (camera, position, options) - camera is +// a required arg, so it can't use createEffectComponent (needs +// `new Effect()` to work with zero args). Built by hand instead, like +// Outline/GodRays. +export function ShockWave(props: ShockWaveProps) { + const { speed, maxRadius, waveSize, amplitude, ref } = props + const { camera } = use(EffectComposerContext) + const effect = useMemo(() => new ShockWaveEffect(camera), [camera]) + const position = useVector3(props, 'position') + + useLiveDefaults(effect, { position, speed, maxRadius, waveSize, amplitude }, LIVE_KEYS) + useDispose(effect) + + return +} diff --git a/src/effects/Texture.tsx b/src/effects/Texture.tsx index 6610b789..e731d2a3 100644 --- a/src/effects/Texture.tsx +++ b/src/effects/Texture.tsx @@ -1,17 +1,22 @@ import { useLoader } from '@react-three/fiber' import { TextureEffect } from 'postprocessing' -import { Ref, useLayoutEffect, useMemo } from 'react' +import type { Ref } from 'react' +import { useLayoutEffect } from 'react' import { RepeatWrapping, SRGBColorSpace, TextureLoader } from 'three' -import { useDispose } from '../util' +import { createEffectComponent, type EffectOptions } from '../createEffectComponent' -type TextureProps = ConstructorParameters[0] & { +const TextureImpl = /* @__PURE__ */ createEffectComponent>( + TextureEffect +) + +export type TextureProps = EffectOptions & { textureSrc: string /** opacity of provided texture */ opacity?: number ref?: Ref } -export function Texture({ textureSrc, texture, opacity = 1, ref, ...props }: TextureProps) { +export function Texture({ textureSrc, texture, opacity = 1, ...props }: TextureProps) { const t = useLoader(TextureLoader, textureSrc) useLayoutEffect(() => { @@ -19,9 +24,5 @@ export function Texture({ textureSrc, texture, opacity = 1, ref, ...props }: Tex t.wrapS = t.wrapT = RepeatWrapping }, [t]) - const effect = useMemo(() => new TextureEffect({ ...props, texture: t || texture }), []) - - useDispose(effect) - - return + return } diff --git a/src/effects/TiltShift.tsx b/src/effects/TiltShift.tsx index 82372e5a..ecd31d10 100644 --- a/src/effects/TiltShift.tsx +++ b/src/effects/TiltShift.tsx @@ -1,4 +1,32 @@ import { BlendFunction, TiltShiftEffect } from 'postprocessing' -import { wrapEffect } from '../wrapEffect' +import type { Ref } from 'react' +import { useMemo } from 'react' +import { createEffectComponent, type EffectOptions } from '../createEffectComponent' -export const TiltShift = /* @__PURE__ */ wrapEffect(TiltShiftEffect, { blendFunction: BlendFunction.ADD }) +type TiltShiftOptions = EffectOptions + +const TiltShiftImpl = /* @__PURE__ */ createEffectComponent(TiltShiftEffect) + +export type TiltShiftProps = TiltShiftOptions & { + opacity?: number + ref?: Ref +} + +// kernelSize/resolutionScale/resolutionX/resolutionY have no live setter in +// postprocessing - routed through args so they still work as plain props +// (previously they were passed as plain props and silently never reached +// the effect at all, since there was no setter for diffProps to hit). +export function TiltShift({ + blendFunction = BlendFunction.ADD, + kernelSize, + resolutionScale, + resolutionX, + resolutionY, + ...liveProps +}: TiltShiftProps) { + const args = useMemo<[TiltShiftOptions]>( + () => [{ kernelSize, resolutionScale, resolutionX, resolutionY }], + [kernelSize, resolutionScale, resolutionX, resolutionY] + ) + return +} diff --git a/src/effects/TiltShift2.tsx b/src/effects/TiltShift2.tsx index 83265060..2da117d2 100644 --- a/src/effects/TiltShift2.tsx +++ b/src/effects/TiltShift2.tsx @@ -1,6 +1,7 @@ import { BlendFunction, Effect, EffectAttribute } from 'postprocessing' +import type { Ref } from 'react' import { Uniform } from 'three' -import { wrapEffect } from '../wrapEffect' +import { createEffectComponent } from '../createEffectComponent' const TiltShiftShader = { fragmentShader: /* glsl */ ` @@ -62,20 +63,22 @@ const TiltShiftShader = { `, } +type Vec2Tuple = [number, number] + export class TiltShiftEffect extends Effect { constructor({ blendFunction = BlendFunction.NORMAL, blur = 0.15, // [0, 1], can go beyond 1 for extra taper = 0.5, // [0, 1], can go beyond 1 for extra - start = [0.5, 0.0], // [0,1] percentage x,y of screenspace - end = [0.5, 1.0], // [0,1] percentage x,y of screenspace + start = [0.5, 0.0] as Vec2Tuple, // [0,1] percentage x,y of screenspace + end = [0.5, 1.0] as Vec2Tuple, // [0,1] percentage x,y of screenspace samples = 10.0, // number of blur samples - direction = [1, 1], // direction of blur + direction = [1, 1] as Vec2Tuple, // direction of blur } = {}) { super('TiltShiftEffect', TiltShiftShader.fragmentShader, { blendFunction, attributes: EffectAttribute.CONVOLUTION, - uniforms: new Map>([ + uniforms: new Map>([ ['blur', new Uniform(blur)], ['taper', new Uniform(taper)], ['start', new Uniform(start)], @@ -85,6 +88,69 @@ export class TiltShiftEffect extends Effect { ]), }) } + + private u(name: string): T { + return this.uniforms.get(name)!.value + } + + private setU(name: string, value: unknown): void { + this.uniforms.get(name)!.value = value + } + + get blur(): number { + return this.u('blur') + } + set blur(value: number) { + this.setU('blur', value) + } + + get taper(): number { + return this.u('taper') + } + set taper(value: number) { + this.setU('taper', value) + } + + get start(): Vec2Tuple { + return this.u('start') + } + set start(value: Vec2Tuple) { + this.setU('start', value) + } + + get end(): Vec2Tuple { + return this.u('end') + } + set end(value: Vec2Tuple) { + this.setU('end', value) + } + + get samples(): number { + return this.u('samples') + } + set samples(value: number) { + this.setU('samples', value) + } + + get direction(): Vec2Tuple { + return this.u('direction') + } + set direction(value: Vec2Tuple) { + this.setU('direction', value) + } +} + +export type TiltShift2Props = { + blendFunction?: BlendFunction + blur?: number + taper?: number + start?: Vec2Tuple + end?: Vec2Tuple + samples?: number + direction?: Vec2Tuple + ref?: Ref } -export const TiltShift2 = /* @__PURE__ */ wrapEffect(TiltShiftEffect, { blendFunction: BlendFunction.NORMAL }) +export const TiltShift2 = /* @__PURE__ */ createEffectComponent( + TiltShiftEffect +) diff --git a/src/effects/ToneMapping.tsx b/src/effects/ToneMapping.tsx index 5358d7b7..2f0fa677 100644 --- a/src/effects/ToneMapping.tsx +++ b/src/effects/ToneMapping.tsx @@ -1,6 +1,19 @@ import { ToneMappingEffect } from 'postprocessing' -import { type EffectProps, wrapEffect } from '../wrapEffect' +import type { Ref } from 'react' +import { useMemo } from 'react' +import { createEffectComponent, type EffectOptions } from '../createEffectComponent' -export type ToneMappingProps = EffectProps +type ToneMappingOptions = EffectOptions -export const ToneMapping = /* @__PURE__ */ wrapEffect(ToneMappingEffect) +const ToneMappingImpl = /* @__PURE__ */ createEffectComponent( + ToneMappingEffect +) + +export type ToneMappingProps = ToneMappingOptions & { opacity?: number; ref?: Ref } + +// minLuminance/maxLuminance have no live setter in postprocessing - routed +// through args so they still work as plain props. +export function ToneMapping({ minLuminance, maxLuminance, ...liveProps }: ToneMappingProps) { + const args = useMemo<[ToneMappingOptions]>(() => [{ minLuminance, maxLuminance }], [minLuminance, maxLuminance]) + return +} diff --git a/src/effects/Vignette.tsx b/src/effects/Vignette.tsx index 886020f5..b9c59068 100644 --- a/src/effects/Vignette.tsx +++ b/src/effects/Vignette.tsx @@ -1,4 +1,7 @@ import { VignetteEffect } from 'postprocessing' -import { wrapEffect } from '../wrapEffect' +import { createEffectComponent, type EffectOptions } from '../createEffectComponent' -export const Vignette = /* @__PURE__ */ wrapEffect(VignetteEffect) +export const Vignette = /* @__PURE__ */ createEffectComponent< + typeof VignetteEffect, + EffectOptions +>(VignetteEffect) diff --git a/src/effects/Water.tsx b/src/effects/Water.tsx index e7b186cd..c4d59c53 100644 --- a/src/effects/Water.tsx +++ b/src/effects/Water.tsx @@ -1,6 +1,7 @@ import { BlendFunction, Effect, EffectAttribute } from 'postprocessing' +import type { Ref } from 'react' import { Uniform } from 'three' -import { wrapEffect } from '../wrapEffect' +import { createEffectComponent } from '../createEffectComponent' const WaterShader = { fragmentShader: /* glsl */ ` @@ -10,7 +11,7 @@ const WaterShader = { vec2 vUv = uv; float frequency = 6.0 * factor; float amplitude = 0.015 * factor; - float x = vUv.y * frequency + time * 0.7; + float x = vUv.y * frequency + time * 0.7; float y = vUv.x * frequency + time * 0.3; vUv.x += cos(x + y) * amplitude * cos(y); vUv.y += sin(x - y) * amplitude * cos(y); @@ -25,11 +26,25 @@ export class WaterEffectImpl extends Effect { super('WaterEffect', WaterShader.fragmentShader, { blendFunction, attributes: EffectAttribute.CONVOLUTION, - uniforms: new Map>([['factor', new Uniform(factor)]]), + uniforms: new Map>([['factor', new Uniform(factor)]]), }) } + + get factor(): number { + return this.uniforms.get('factor')!.value + } + + set factor(value: number) { + this.uniforms.get('factor')!.value = value + } +} + +export type WaterEffectProps = { + blendFunction?: BlendFunction + factor?: number + ref?: Ref } -export const WaterEffect = /* @__PURE__ */ wrapEffect(WaterEffectImpl, { - blendFunction: BlendFunction.NORMAL, -}) +export const WaterEffect = /* @__PURE__ */ createEffectComponent( + WaterEffectImpl +) diff --git a/src/index.ts b/src/index.ts index 492432c9..860ac4d1 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,4 +1,6 @@ +export * from './createEffectComponent' export * from './EffectComposer' +export * from './EffectGroup' export * from './Selection' export * from './util' export * from './wrapEffect' @@ -37,5 +39,5 @@ export * from './effects/ToneMapping' export * from './effects/Vignette' export * from './effects/Water' -// These are not effect passes -export * from './effects/N8AO' +export * from './passes/DepthPicking' +export * from './passes/N8AO' diff --git a/src/passes/DepthPicking.tsx b/src/passes/DepthPicking.tsx new file mode 100644 index 00000000..b9bf1774 --- /dev/null +++ b/src/passes/DepthPicking.tsx @@ -0,0 +1,63 @@ +import { useThree } from '@react-three/fiber' +import { CopyPass, DepthPickingPass as DepthPickingPassImpl } from 'postprocessing' +import { use, useCallback, useEffect, useImperativeHandle, useState, type Ref } from 'react' +import type { Camera } from 'three' +import { Vector2, Vector3 } from 'three' +import { EffectComposerContext } from '../EffectComposer' +import { useDispose } from '../util' + +export type DepthPickingApi = { + readDepth: (ndc: Vector2 | Vector3) => Promise +} + +export type DepthPickingProps = { + ref?: Ref +} + +// Mounts a vanilla `postprocessing` DepthPickingPass and exposes its +// `readDepth` via ref - nothing else. Renders nothing. Pair with +// `useDepthPicking` for a world-space position instead of raw depth. +export function DepthPicking({ ref }: DepthPickingProps) { + const { composer } = use(EffectComposerContext) + + const [depthPickingPass] = useState(() => new DepthPickingPassImpl()) + const [copyPass] = useState(() => new CopyPass()) + useEffect(() => { + composer.addPass(depthPickingPass) + composer.addPass(copyPass) + return () => { + composer.removePass(depthPickingPass) + composer.removePass(copyPass) + } + }, [composer, depthPickingPass, copyPass]) + + useDispose(depthPickingPass) + useDispose(copyPass) + + useImperativeHandle(ref, () => ({ readDepth: (ndc) => depthPickingPass.readDepth(ndc) }), [depthPickingPass]) + + return null +} + +export function useDepthPicking(pass: React.RefObject, camera?: Camera) { + const composerCamera = use(EffectComposerContext)?.camera + const defaultCamera = useThree((state) => state.camera) + const resolvedCamera = camera ?? composerCamera ?? defaultCamera + const [ndc] = useState(() => new Vector3()) + + return useCallback( + async (x: number, y: number): Promise => { + if (!pass.current) return false + ndc.x = x + ndc.y = y + ndc.z = await pass.current.readDepth(ndc) + ndc.z = ndc.z * 2.0 - 1.0 + const hit = 1 - ndc.z > 0.0000001 // missed if ndc.z is close to 1 + // clone - unproject mutates in place, and ndc is reused across calls, + // so returning it directly would hand out a reference that changes + // under the caller on the next pick. + return hit ? ndc.clone().unproject(resolvedCamera) : false + }, + [pass, resolvedCamera, ndc] + ) +} diff --git a/src/effects/N8AO.tsx b/src/passes/N8AO.tsx similarity index 59% rename from src/effects/N8AO.tsx rename to src/passes/N8AO.tsx index 2c68a726..f0f52c9f 100644 --- a/src/effects/N8AO.tsx +++ b/src/passes/N8AO.tsx @@ -5,9 +5,11 @@ import { ReactThreeFiber, applyProps, useThree } from '@react-three/fiber' import { Ref, useLayoutEffect, useMemo } from 'react' /* @ts-ignore */ import { N8AOPostPass } from 'n8ao' +import { groupPasses } from '../EffectComposer' import { useDispose } from '../util' export type N8AOProps = { + enabled?: boolean aoRadius?: number distanceFalloff?: number intensity?: number @@ -24,6 +26,7 @@ export type N8AOProps = { } export function N8AO({ + enabled = true, halfRes, screenSpaceRadius, quality, @@ -38,12 +41,26 @@ export function N8AO({ renderMode = 0, ref, }: N8AOProps) { - const { camera, scene } = useThree() - const effect = useMemo(() => new N8AOPostPass(scene, camera), [camera, scene]) + const { camera, scene, invalidate } = useThree() + const effect = useMemo(() => { + const instance = new N8AOPostPass(scene, camera) + // N8AOPostPass isn't a postprocessing Effect, so it's not mergeable via + // EffectGroup - it's already a standalone Pass, hence its own `enabled` + // prop below instead. Registering it here still gets it the shared + // trailing CopyPass safety net (see EffectComposer.tsx) so disabling it + // while it's the last pass in the chain doesn't blank the canvas. + groupPasses.add(instance) + return instance + }, [camera, scene]) // TODO: implement dispose upstream; this effect has memory leaks without useDispose(effect) + useLayoutEffect(() => { + effect.enabled = enabled + invalidate() + }, [effect, enabled, invalidate]) + useLayoutEffect(() => { applyProps(effect.configuration, { color, @@ -58,6 +75,9 @@ export function N8AO({ halfRes, depthAwareUpsampling, }) + // effect.configuration is a plain object, never r3f-managed - applyProps' + // own invalidate (gated behind object.__r3f) never fires for it. + invalidate() }, [ screenSpaceRadius, color, @@ -71,11 +91,15 @@ export function N8AO({ halfRes, depthAwareUpsampling, effect, + invalidate, ]) useLayoutEffect(() => { - if (quality) effect.setQualityMode(quality.charAt(0).toUpperCase() + quality.slice(1)) - }, [effect, quality]) + if (quality) { + effect.setQualityMode(quality.charAt(0).toUpperCase() + quality.slice(1)) + invalidate() + } + }, [effect, quality, invalidate]) return } diff --git a/src/tests/Autofocus.test.tsx b/src/tests/Autofocus.test.tsx new file mode 100644 index 00000000..64c2b674 --- /dev/null +++ b/src/tests/Autofocus.test.tsx @@ -0,0 +1,38 @@ +import { DepthPickingPass, EffectComposer as EffectComposerImpl, EffectPass, RenderPass } from 'postprocessing' +import * as React from 'react' +import { describe, expect, it } from 'vitest' +import { EffectComposer } from '../EffectComposer' +import { Autofocus } from '../effects/Autofocus' +import { flush, root, strict, waitForComposer } from './test-utils' + +describe('Autofocus', () => { + it('inserts depthPickingPass right after RenderPass, not appended at the end', async () => { + const composerRef = React.createRef() + + await React.act(async () => root.render({})) + const composer = await waitForComposer(composerRef) + await flush() + + expect(composer.passes[0]).toBeInstanceOf(RenderPass) + expect(composer.passes[1]).toBeInstanceOf(DepthPickingPass) + + await React.act(async () => root.render(null)) + }) + + it('never lets depthPickingPass own renderToScreen, regardless of StrictMode double-invocation', async () => { + const composerRef = React.createRef() + + await React.act(async () => root.render(strict({}))) + const composer = await waitForComposer(composerRef) + for (let i = 0; i < 10; i++) await flush() + + const depthPickingPass = composer.passes.find((p) => p instanceof DepthPickingPass)! + expect(depthPickingPass.renderToScreen).toBe(false) + + // The real, visible output (DepthOfField's own EffectPass) must own it instead. + const effectPass = composer.passes.find((p) => p instanceof EffectPass)! + expect(effectPass.renderToScreen).toBe(true) + + await React.act(async () => root.render(null)) + }) +}) diff --git a/src/tests/Bloom.test.tsx b/src/tests/Bloom.test.tsx new file mode 100644 index 00000000..facb2019 --- /dev/null +++ b/src/tests/Bloom.test.tsx @@ -0,0 +1,76 @@ +import { BloomEffect, EffectComposer as EffectComposerImpl } from 'postprocessing' +import * as React from 'react' +import { describe, expect, it } from 'vitest' +import { EffectComposer } from '../EffectComposer' +import { Bloom } from '../effects/Bloom' +import { flush, root } from './test-utils' + +describe('Bloom', () => { + it('applies intensity live, without reconstructing the effect', async () => { + const composerRef = React.createRef() + const ref = React.createRef() + + const render = (intensity: number) => + root.render( + + + + ) + + await React.act(async () => render(1)) + await flush() + const first = ref.current + expect(first!.intensity).toBe(1) + + await React.act(async () => render(2)) + await flush() + + expect(ref.current).toBe(first) + expect(ref.current!.intensity).toBe(2) + + await React.act(async () => root.render(null)) + }) + + it('applies mipmapBlur (a construction-only option) as a plain prop, reconstructing under the hood', async () => { + const composerRef = React.createRef() + const ref = React.createRef() + + const render = (mipmapBlur: boolean) => + root.render( + + + + ) + + await React.act(async () => render(true)) + await flush() + const first = ref.current + expect(first!.mipmapBlurPass.enabled).toBe(true) + + await React.act(async () => render(false)) + await flush() + + expect(ref.current).not.toBe(first) + expect(ref.current!.mipmapBlurPass.enabled).toBe(false) + + await React.act(async () => root.render(null)) + }) + + it('accepts opacity, as documented in the README (#opacity narrower than createEffectComponent allows)', async () => { + const composerRef = React.createRef() + const ref = React.createRef() + + await React.act(async () => + root.render( + + + + ) + ) + await flush() + + expect(ref.current!.blendMode.opacity.value).toBe(0.02) + + await React.act(async () => root.render(null)) + }) +}) diff --git a/src/tests/ChromaticAberration.test.tsx b/src/tests/ChromaticAberration.test.tsx index 5e7c5d16..d98d87c7 100644 --- a/src/tests/ChromaticAberration.test.tsx +++ b/src/tests/ChromaticAberration.test.tsx @@ -28,4 +28,54 @@ describe('ChromaticAberration', () => { await React.act(async () => root.render(null)) }) + + it('applies offset live without reconstructing the effect', async () => { + const composerRef = React.createRef() + const ref = React.createRef() + + const render = (x: number) => + root.render( + + + + ) + + await React.act(async () => render(0.01)) + await flush() + const first = ref.current + expect(first!.offset.x).toBeCloseTo(0.01) + + await React.act(async () => render(0.02)) + await flush() + + expect(ref.current).toBe(first) + expect(ref.current!.offset.x).toBeCloseTo(0.02) + + await React.act(async () => root.render(null)) + }) + + it('applies radialModulation live, without reconstructing the effect', async () => { + const composerRef = React.createRef() + const ref = React.createRef() + + const render = (radialModulation: boolean) => + root.render( + + + + ) + + await React.act(async () => render(false)) + await flush() + const first = ref.current + expect(first!.radialModulation).toBe(false) + + await React.act(async () => render(true)) + await flush() + + expect(ref.current).toBe(first) + expect(ref.current!.radialModulation).toBe(true) + + await React.act(async () => root.render(null)) + }) }) diff --git a/src/tests/ColorDepth.test.tsx b/src/tests/ColorDepth.test.tsx new file mode 100644 index 00000000..e3b3ea0f --- /dev/null +++ b/src/tests/ColorDepth.test.tsx @@ -0,0 +1,71 @@ +import { ColorDepthEffect, EffectComposer as EffectComposerImpl } from 'postprocessing' +import * as React from 'react' +import { describe, expect, it } from 'vitest' +import { ColorDepth } from '../effects/ColorDepth' +import { EffectComposer } from '../EffectComposer' +import { flush, root } from './test-utils' + +describe('ColorDepth', () => { + it('applies bits live via the differently-named bitDepth setter, without reconstructing the effect', async () => { + const composerRef = React.createRef() + const ref = React.createRef() + + const render = (bits: number) => + root.render( + + + + ) + + await React.act(async () => render(4)) + await flush() + const first = ref.current + expect(first!.bitDepth).toBe(4) + + await React.act(async () => render(8)) + await flush() + + expect(ref.current).toBe(first) + expect(ref.current!.bitDepth).toBe(8) + + await React.act(async () => root.render(null)) + }) + + it('resets bitDepth to its constructor default when bits is removed', async () => { + const composerRef = React.createRef() + const ref = React.createRef() + + await React.act(async () => + root.render( + + + + ) + ) + await flush() + const defaultBitDepth = ref.current!.bitDepth + + await React.act(async () => + root.render( + + + + ) + ) + await flush() + expect(ref.current!.bitDepth).toBe(4) + + await React.act(async () => + root.render( + + + + ) + ) + await flush() + + expect(ref.current!.bitDepth).toBe(defaultBitDepth) + + await React.act(async () => root.render(null)) + }) +}) diff --git a/src/tests/DepthOfField.test.tsx b/src/tests/DepthOfField.test.tsx new file mode 100644 index 00000000..8b0545da --- /dev/null +++ b/src/tests/DepthOfField.test.tsx @@ -0,0 +1,130 @@ +import { DepthOfFieldEffect, EffectComposer as EffectComposerImpl } from 'postprocessing' +import * as React from 'react' +import { Texture } from 'three' +import { describe, expect, it } from 'vitest' +import { EffectComposer } from '../EffectComposer' +import { DepthOfField } from '../effects/DepthOfField' +import { flush, root, waitForComposer } from './test-utils' + +describe('DepthOfField', () => { + it('applies bokehScale live, without reconstructing the effect', async () => { + const composerRef = React.createRef() + const ref = React.createRef() + + const render = (bokehScale: number) => + root.render( + + + + ) + + await React.act(async () => render(1)) + await waitForComposer(composerRef) + await flush() + const first = ref.current + expect(first!.bokehScale).toBe(1) + + await React.act(async () => render(2)) + await flush() + + expect(ref.current).toBe(first) + expect(ref.current!.bokehScale).toBe(2) + + await React.act(async () => root.render(null)) + }) + + it('applies focusDistance live via the nested cocMaterial, without reconstructing the effect', async () => { + const composerRef = React.createRef() + const ref = React.createRef() + + const render = (focusDistance: number) => + root.render( + + + + ) + + await React.act(async () => render(0.1)) + await waitForComposer(composerRef) + await flush() + const first = ref.current + expect(first!.cocMaterial.focusDistance).toBeCloseTo(0.1) + + await React.act(async () => render(0.5)) + await flush() + + expect(ref.current).toBe(first) + expect(ref.current!.cocMaterial.focusDistance).toBeCloseTo(0.5) + + await React.act(async () => root.render(null)) + }) + + it('applies depthTexture live via setDepthTexture, without reconstructing, and resets on removal', async () => { + const composerRef = React.createRef() + const ref = React.createRef() + const textureA = new Texture() + const textureB = new Texture() + // cocMaterial.depthBuffer is write-only in postprocessing (setter, no + // getter) - the current value only reads back through its own uniform. + const currentDepthBuffer = () => + (ref.current!.cocMaterial as unknown as { uniforms: { depthBuffer: { value: unknown } } }).uniforms.depthBuffer + .value + + const render = (depthTexture?: { texture: Texture; packing: number }) => + root.render( + + + + ) + + await React.act(async () => render()) + await waitForComposer(composerRef) + await flush() + const first = ref.current + + await React.act(async () => render({ texture: textureA, packing: 0 })) + await flush() + expect(ref.current).toBe(first) + expect(currentDepthBuffer()).toBe(textureA) + + await React.act(async () => render({ texture: textureB, packing: 0 })) + await flush() + expect(ref.current).toBe(first) + expect(currentDepthBuffer()).toBe(textureB) + + await React.act(async () => render()) + await flush() + expect(ref.current).toBe(first) + // Reverts to no manually-provided depth texture (undefined), the state + // useLiveDefaults captured as this instance's default on first apply - + // not whatever EffectComposer's own depth-attribute auto-wiring later + // assigns, which runs separately and after this. + expect(currentDepthBuffer()).toBeUndefined() + + await React.act(async () => root.render(null)) + }) + + it('still reconstructs when resolutionScale (construction-only) changes', async () => { + const composerRef = React.createRef() + const ref = React.createRef() + + const render = (resolutionScale: number) => + root.render( + + + + ) + + await React.act(async () => render(0.5)) + await waitForComposer(composerRef) + await flush() + const first = ref.current + + await React.act(async () => render(1)) + await flush() + + expect(ref.current).not.toBe(first) + + await React.act(async () => root.render(null)) + }) +}) diff --git a/src/tests/DepthPicking.test.tsx b/src/tests/DepthPicking.test.tsx new file mode 100644 index 00000000..4aa04f17 --- /dev/null +++ b/src/tests/DepthPicking.test.tsx @@ -0,0 +1,250 @@ +import { useThree } from '@react-three/fiber' +import { DepthPickingPass as DepthPickingPassImpl, EffectComposer as EffectComposerImpl, EffectPass, RenderPass } from 'postprocessing' +import * as React from 'react' +import * as THREE from 'three' +import { describe, expect, it } from 'vitest' +import { EffectComposer } from '../EffectComposer' +import { Noise } from '../effects/Noise' +import { DepthPicking, useDepthPicking, type DepthPickingApi } from '../passes/DepthPicking' +import { flush, root, strict, waitForComposer } from './test-utils' + +describe('DepthPicking', () => { + it('adds its pass after RenderPass', async () => { + const composerRef = React.createRef() + + await React.act(async () => root.render({})) + const composer = await waitForComposer(composerRef) + await flush() + + expect(composer.passes[0]).toBeInstanceOf(RenderPass) + expect(composer.passes.slice(1)).toContainEqual(expect.any(DepthPickingPassImpl)) + + await React.act(async () => root.render(null)) + }) + + // Depth reads are position-independent (postprocessing's stable depth + // texture is populated once per frame off RenderPass, not off wherever + // DepthPicking's own pass happens to land) - verified against real + // geometry in test-env, not just structurally here. enableNormalPass adds + // a NormalPass right after RenderPass too, shifting what "index 1" used + // to mean back when this was added at a fixed index. + it('still keeps its trailing CopyPass owning renderToScreen with enableNormalPass and other effects around it', async () => { + const composerRef = React.createRef() + + await React.act(async () => + root.render( + + + + + + ) + ) + const composer = await waitForComposer(composerRef) + await flush() + + const depthPickingPass = composer.passes.find((p) => p instanceof DepthPickingPassImpl)! + expect(depthPickingPass.renderToScreen).toBe(false) + expect(composer.passes.at(-1)!.renderToScreen).toBe(true) + + await React.act(async () => root.render(null)) + }) + + it('never lets its pass own renderToScreen when a real effect follows it, regardless of StrictMode', async () => { + const composerRef = React.createRef() + + await React.act(async () => + root.render( + strict( + + + + + ) + ) + ) + const composer = await waitForComposer(composerRef) + for (let i = 0; i < 10; i++) await flush() + + const pass = composer.passes.find((p) => p instanceof DepthPickingPassImpl)! + expect(pass.renderToScreen).toBe(false) + + // The real, visible output (Noise's own EffectPass) must own it instead. + const effectPass = composer.passes.find((p) => p instanceof EffectPass)! + expect(effectPass.renderToScreen).toBe(true) + + await React.act(async () => root.render(null)) + }) + + // DepthPickingPass.render() is conditional on a pending readDepth() call - + // unlike a normal pass, a frame with nothing pending renders nothing at + // all. If it ever owned renderToScreen (e.g. as the structurally-last + // pass with no other effects present), those frames would leave the + // screen showing whatever was already in the framebuffer. Its own + // trailing CopyPass (always unconditional) must own renderToScreen + // instead, even with no other effects around. + it('never owns renderToScreen even with no other effects - its own trailing CopyPass does instead', async () => { + const composerRef = React.createRef() + + await React.act(async () => root.render({})) + const composer = await waitForComposer(composerRef) + await flush() + + expect(composer.passes).toHaveLength(3) + const depthPickingPass = composer.passes.find((p) => p instanceof DepthPickingPassImpl)! + expect(depthPickingPass.renderToScreen).toBe(false) + expect(composer.passes.at(-1)!.renderToScreen).toBe(true) + expect(composer.passes.at(-1)).not.toBeInstanceOf(DepthPickingPassImpl) + + await React.act(async () => root.render(null)) + }) + + it('exposes readDepth via ref and renders nothing itself', async () => { + const ref = React.createRef() + + await React.act(async () => + root.render( + + + + ) + ) + await flush() + + expect(ref.current).toBeTruthy() + expect(typeof ref.current!.readDepth).toBe('function') + }) +}) + +describe('useDepthPicking', () => { + function Picker({ + passRef, + camera, + onReady, + }: { + passRef: React.RefObject + camera?: THREE.Camera + onReady: (getHit: ReturnType) => void + }) { + const getHit = useDepthPicking(passRef, camera) + onReady(getHit) + return null + } + + it('unprojects a picked depth into a world-space point using an explicitly passed camera', async () => { + const camera = new THREE.PerspectiveCamera(50, 1, 0.1, 1000) + camera.position.set(0, 0, 5) + camera.updateMatrixWorld() + camera.updateProjectionMatrix() + + // A stand-in for the mounted pass - readDepth resolves to a fixed, + // known depth so the resulting world position is fully predictable. + const fakePass: DepthPickingApi = { readDepth: async () => 0.5 } + const passRef = { current: fakePass } + + let getHit: ReturnType = null! + + await React.act(async () => + root.render( + + (getHit = fn)} /> + + ) + ) + await flush() + + const hit = await getHit(0, 0) + expect(hit).not.toBe(false) + + const expected = new THREE.Vector3(0, 0, 0.5 * 2 - 1).unproject(camera) + expect((hit as THREE.Vector3).toArray()).toEqual(expected.toArray()) + }) + + it("falls back to r3f's own default camera when none is passed explicitly", async () => { + const fakePass: DepthPickingApi = { readDepth: async () => 0.5 } + const passRef = { current: fakePass } + + let getHit: ReturnType = null! + let defaultCamera: THREE.Camera | null = null + + function CaptureDefaultCamera() { + defaultCamera = useThree((state) => state.camera) + return null + } + + await React.act(async () => + root.render( + + + (getHit = fn)} /> + + ) + ) + await flush() + + const hit = await getHit(0, 0) + expect(hit).not.toBe(false) + + const expected = new THREE.Vector3(0, 0, 0.5 * 2 - 1).unproject(defaultCamera!) + expect((hit as THREE.Vector3).toArray()).toEqual(expected.toArray()) + }) + + // The point of taking `pass` as a plain ref (rather than reading + // DepthPicking's own context) - the hook itself never touches + // EffectComposerContext, so it works from anywhere under , not + // just from inside the the pass happens to live in. + it('works when called outside the the pass is mounted in', async () => { + const fakePass: DepthPickingApi = { readDepth: async () => 0.5 } + const passRef = { current: fakePass } + let getHit: ReturnType = null! + + await React.act(async () => + root.render( + <> + + + + (getHit = fn)} /> + + ) + ) + await flush() + + expect(await getHit(0, 0)).not.toBe(false) + }) + + it('returns false when depth is at the far plane (nothing hit)', async () => { + const camera = new THREE.PerspectiveCamera(50, 1, 0.1, 1000) + const fakePass: DepthPickingApi = { readDepth: async () => 1 } + const passRef = { current: fakePass } + + let getHit: ReturnType = null! + + await React.act(async () => + root.render( + + (getHit = fn)} /> + + ) + ) + await flush() + + expect(await getHit(0, 0)).toBe(false) + }) + + it('returns false when the pass ref is not attached yet', async () => { + const passRef = { current: null } + let getHit: ReturnType = null! + + await React.act(async () => + root.render( + + (getHit = fn)} /> + + ) + ) + await flush() + + expect(await getHit(0, 0)).toBe(false) + }) +}) diff --git a/src/tests/EffectComposer.test.tsx b/src/tests/EffectComposer.test.tsx index 709c515d..1e5adc8a 100644 --- a/src/tests/EffectComposer.test.tsx +++ b/src/tests/EffectComposer.test.tsx @@ -3,6 +3,7 @@ import { ColorAverageEffect, DepthDownsamplingPass, Effect, + EffectAttribute, EffectComposer as EffectComposerImpl, EffectPass, NormalPass, @@ -34,9 +35,23 @@ class EffectC extends Effect { } } +class ConvolutionEffect extends Effect { + constructor() { + super('ConvolutionEffect', EFFECT_SHADER, { attributes: EffectAttribute.CONVOLUTION }) + } +} + +class ConvolutionEffectTwo extends Effect { + constructor() { + super('ConvolutionEffectTwo', EFFECT_SHADER, { attributes: EffectAttribute.CONVOLUTION }) + } +} + const WrappedEffectA = wrapEffect(EffectA) const WrappedEffectB = wrapEffect(EffectB) const WrappedEffectC = wrapEffect(EffectC) +const WrappedConvolutionEffect = wrapEffect(ConvolutionEffect) +const WrappedConvolutionEffectTwo = wrapEffect(ConvolutionEffectTwo) afterEach(async () => { await React.act(async () => { @@ -299,6 +314,285 @@ describe('EffectComposer', () => { }) }) + describe('autoRenderToScreen', () => { + it('defaults to true, with the last pass rendering to screen', async () => { + const ref = React.createRef() + + await React.act(async () => + root.render( + + + + ) + ) + + const composer = await waitForComposer(ref) + expect(composer.autoRenderToScreen).toBe(true) + expect(composer.passes.at(-1)?.renderToScreen).toBe(true) + }) + + it('applies false at mount, with no pass rendering to screen', async () => { + const ref = React.createRef() + + await React.act(async () => + root.render( + + + + ) + ) + + const composer = await waitForComposer(ref) + expect(composer.autoRenderToScreen).toBe(false) + expect(composer.passes.at(-1)?.renderToScreen).toBe(false) + }) + + // Not a constructor option in postprocessing itself, but the only place + // it's actually read is inside addPass() - deciding whether *that* call + // also assigns renderToScreen to the pass going in. Setting it after + // passes already exist doesn't revisit them, so treating this as a + // live/reactive prop (mutate the flag, leave existing passes alone) + // would leave a composer that was built with autoRenderToScreen: false + // permanently unable to render to screen again, even after the prop + // flips back to true - every existing pass already has renderToScreen: + // false baked in from when it was added, and nothing revisits it. + // Recreating the composer (same treatment as depthBuffer/multisampling/ + // etc. above) sidesteps that entirely: every pass is freshly added + // through addPass() with the current flag already in effect. + it('recreates the composer when autoRenderToScreen changes, with the new last pass matching it', async () => { + const ref = React.createRef() + + await React.act(async () => + root.render( + + + + ) + ) + + const first = await waitForComposer(ref) + expect(first.passes.at(-1)?.renderToScreen).toBe(false) + + await React.act(async () => + root.render( + + + + ) + ) + + const second = await waitForNewComposer(ref, first) + expect(second.autoRenderToScreen).toBe(true) + expect(second.passes.at(-1)?.renderToScreen).toBe(true) + }) + }) + + describe('renderPass', () => { + it('adds a plain RenderPass as the first pass by default', async () => { + const ref = React.createRef() + + await React.act(async () => + root.render( + + + + ) + ) + + const composer = await waitForComposer(ref) + expect(composer.passes[0]).toBeInstanceOf(RenderPass) + }) + + it('uses a custom factory instead of the default RenderPass, called with the resolved scene/camera', async () => { + class CustomRenderPass extends RenderPass {} + const scene = new THREE.Scene() + const camera = new THREE.PerspectiveCamera() + const factory = vi.fn((s: THREE.Scene, c: THREE.Camera) => new CustomRenderPass(s, c)) + const ref = React.createRef() + + await React.act(async () => + root.render( + + + + ) + ) + + const composer = await waitForComposer(ref) + expect(composer.passes[0]).toBeInstanceOf(CustomRenderPass) + expect(factory).toHaveBeenCalledWith(scene, camera) + }) + + // Not a postprocessing constructor option (it's this library's own prop), + // but the pass it produces is only ever added once, at construction - + // same treatment as depthBuffer/multisampling/autoRenderToScreen above. + it('recreates the composer when renderPass changes', async () => { + class CustomRenderPass extends RenderPass {} + const ref = React.createRef() + + await React.act(async () => + root.render( + + + + ) + ) + + const first = await waitForComposer(ref) + expect(first.passes[0]).toBeInstanceOf(RenderPass) + expect(first.passes[0]).not.toBeInstanceOf(CustomRenderPass) + + await React.act(async () => + root.render( + new CustomRenderPass(s, c)}> + + + ) + ) + + const second = await waitForNewComposer(ref, first) + expect(second.passes[0]).toBeInstanceOf(CustomRenderPass) + }) + }) + + describe('mergeMode', () => { + it("'auto' (default) merges a convolution effect with its non-convolution neighbors into one EffectPass", async () => { + const ref = React.createRef() + + await React.act(async () => + root.render( + + + + + + + ) + ) + + const composer = await waitForComposer(ref) + const effectPasses = composer.passes.filter((pass) => pass instanceof EffectPass) + expect(effectPasses).toHaveLength(1) + // @ts-expect-error - `effects` isn't part of the public Pass typing + expect(effectPasses[0].effects).toHaveLength(4) + }) + + it("'auto' still splits into separate passes rather than merging two convolution effects together", async () => { + const ref = React.createRef() + + await React.act(async () => + root.render( + + + + + + + ) + ) + + const composer = await waitForComposer(ref) + const effectPasses = composer.passes.filter((pass) => pass instanceof EffectPass) + // [A, ConvolutionEffect] | [ConvolutionEffectTwo, B] - never both convolutions in one pass + expect(effectPasses).toHaveLength(2) + // @ts-expect-error - `effects` isn't part of the public Pass typing + const firstEffects = effectPasses[0].effects as Effect[] + // @ts-expect-error - `effects` isn't part of the public Pass typing + const secondEffects = effectPasses[1].effects as Effect[] + expect(firstEffects.filter((e) => e instanceof ConvolutionEffect || e instanceof ConvolutionEffectTwo)).toHaveLength(1) + expect(secondEffects.filter((e) => e instanceof ConvolutionEffect || e instanceof ConvolutionEffectTwo)).toHaveLength(1) + }) + + it("'all' merges even a single convolution effect with neighbors, same as 'auto'", async () => { + const ref = React.createRef() + + await React.act(async () => + root.render( + + + + + + ) + ) + + const composer = await waitForComposer(ref) + const effectPasses = composer.passes.filter((pass) => pass instanceof EffectPass) + expect(effectPasses).toHaveLength(1) + // @ts-expect-error - `effects` isn't part of the public Pass typing + expect(effectPasses[0].effects).toHaveLength(3) + }) + + // postprocessing itself throws when two convolution effects share a + // pass - 'all' doesn't guard against that (same no-guardrail contract as + // EffectGroup), unlike 'auto' which keeps them apart specifically to + // avoid this. + it("'all' throws at render time if that removes 'auto'-only protection against merging two convolution effects", async () => { + const ref = React.createRef() + + await expect( + React.act(async () => + root.render( + + + + + + + ) + ) + ).rejects.toThrow('Convolution effects cannot be merged') + }) + + it("'none' gives every effect its own EffectPass, even non-convolution ones", async () => { + const ref = React.createRef() + + await React.act(async () => + root.render( + + + + + + ) + ) + + const composer = await waitForComposer(ref) + expect(composer.passes.filter((pass) => pass instanceof EffectPass)).toHaveLength(3) + }) + + it('rebuilds passes (without recreating the composer) when mergeMode changes', async () => { + const ref = React.createRef() + + await React.act(async () => + root.render( + + + + + + ) + ) + + const composer = await waitForComposer(ref) + expect(composer.passes.filter((pass) => pass instanceof EffectPass)).toHaveLength(3) + + await React.act(async () => + root.render( + + + + + + ) + ) + await flush() + + expect(await waitForComposer(ref)).toBe(composer) + expect(composer.passes.filter((pass) => pass instanceof EffectPass)).toHaveLength(1) + }) + }) + describe('cleanup and disposal', () => { it('unregisters effects and clears the ref on full unmount', async () => { const ref = React.createRef() @@ -380,6 +674,128 @@ describe('EffectComposer', () => { disposeSpy.mockRestore() }) + it('disposes a discarded EffectPass wrapper\'s own material on rebuild, without disposing the effects it wrapped', async () => { + const ref = React.createRef() + + await React.act(async () => root.render()) + const composer = await waitForComposer(ref) + await waitForEffects(ref, 1) + + const firstPass = composer.passes.find((p) => p instanceof EffectPass) as EffectPass + const materialDisposeSpy = vi.spyOn(firstPass.fullscreenMaterial, 'dispose') + const effectDisposeSpy = vi.spyOn(EffectA.prototype, 'dispose') + + // Changing the node list forces a rebuild: buildPasses always + // constructs a brand new EffectPass, discarding the old wrapper. + await React.act(async () => + root.render( + + + + + ) + ) + await flush() + + const secondPass = composer.passes.find((p) => p instanceof EffectPass) as EffectPass + expect(secondPass).not.toBe(firstPass) + expect(materialDisposeSpy).toHaveBeenCalledTimes(1) + expect(effectDisposeSpy).not.toHaveBeenCalled() + + materialDisposeSpy.mockRestore() + effectDisposeSpy.mockRestore() + }) + + it('detaches a discarded EffectPass\'s change listener from the effect it wrapped, so it no longer reacts to it', async () => { + const ref = React.createRef() + const effectRef = React.createRef() + + await React.act(async () => root.render()) + const composer = await waitForComposer(ref) + await waitForEffects(ref, 1) + + const firstPass = composer.passes.find((p) => p instanceof EffectPass) as EffectPass + const recompileSpy = vi.spyOn(firstPass, 'recompile') + + await React.act(async () => + root.render( + + + + + ) + ) + await flush() + + const secondPass = composer.passes.find((p) => p instanceof EffectPass) as EffectPass + expect(secondPass).not.toBe(firstPass) + + // The same effect instance survived the rebuild - firing its own + // 'change' event should only reach whatever pass currently wraps it, + // not the discarded one still listening from before. + effectRef.current!.dispatchEvent({ type: 'change' }) + + expect(recompileSpy).not.toHaveBeenCalled() + + recompileSpy.mockRestore() + }) + + it('leaves a user-provided EffectPass (rendered directly as a child) untouched across a rebuild', async () => { + const ref = React.createRef() + const camera = new THREE.PerspectiveCamera() + const userEffect = new EffectC() + const userPass = new EffectPass(camera, userEffect) + + await React.act(async () => + root.render( + + + + + ) + ) + const composer = await waitForComposer(ref) + await waitForEffects(ref, 1) + expect(composer.passes).toContain(userPass) + + // Forces a rebuild (node list changes) - buildPasses only ever + // constructs a *new* EffectPass for Effect children; userPass is + // passed through unchanged via the plain-Pass branch. + await React.act(async () => + root.render( + + + + + + ) + ) + await flush() + + expect(composer.passes).toContain(userPass) + // @ts-expect-error - `effects` isn't part of the public Pass typing + expect(userPass.effects).toEqual([userEffect]) + + await React.act(async () => root.render(null)) + }) + + it('disposes the final EffectPass wrapper\'s material on full unmount too (composer.dispose has nothing left to dispose by then)', async () => { + const ref = React.createRef() + + await React.act(async () => root.render()) + const composer = await waitForComposer(ref) + await waitForEffects(ref, 1) + + const pass = composer.passes.find((p) => p instanceof EffectPass) as EffectPass + const materialDisposeSpy = vi.spyOn(pass.fullscreenMaterial, 'dispose') + + await React.act(async () => root.render(null)) + + expect(materialDisposeSpy).toHaveBeenCalled() + + materialDisposeSpy.mockRestore() + }) + it('disposes exactly as many composers as it constructs, across repeated prop changes', async () => { const ref = React.createRef() const disposeSpy = vi.spyOn(EffectComposerImpl.prototype, 'dispose') @@ -405,6 +821,42 @@ describe('EffectComposer', () => { disposeSpy.mockRestore() }) + it('does not dispose a still-in-use effect when a composer-level prop (multisampling) recreates the composer', async () => { + const ref = React.createRef() + const effectRef = React.createRef() + + await React.act(async () => + root.render( + + + + ) + ) + const firstComposer = await waitForComposer(ref) + await waitForEffects(ref, 1) + const effect = effectRef.current + expect(effect).toBeTruthy() + + const effectDisposeSpy = vi.spyOn(EffectA.prototype, 'dispose') + + await React.act(async () => + root.render( + + + + ) + ) + const secondComposer = await waitForNewComposer(ref, firstComposer) + await flush() + + expect(secondComposer).not.toBe(firstComposer) + expect(effectRef.current).toBe(effect) + expect(effectDisposeSpy).not.toHaveBeenCalled() + expect(secondComposer.passes.some((p) => p instanceof EffectPass)).toBe(true) + + effectDisposeSpy.mockRestore() + }) + it('disposes a hand-constructed effect exactly once on unmount', async () => { const disposeSpy = vi.spyOn(ColorAverageEffect.prototype, 'dispose') const ref = React.createRef() @@ -425,7 +877,7 @@ describe('EffectComposer', () => { disposeSpy.mockRestore() }) - it('disposes exactly as many ColorAverage instances as it constructs, across repeated prop changes', async () => { + it('keeps a single ColorAverage instance across repeated blendFunction changes and disposes it exactly once (blendFunction is live, not construction-only)', async () => { const disposeSpy = vi.spyOn(ColorAverageEffect.prototype, 'dispose') const ref = React.createRef() const seenInstances = new Set() @@ -446,15 +898,16 @@ describe('EffectComposer', () => { await React.act(async () => root.render(null)) - expect(seenInstances.size).toBe(cycles) - expect(disposeSpy).toHaveBeenCalledTimes(cycles) + expect(seenInstances.size).toBe(1) + expect(disposeSpy).toHaveBeenCalledTimes(1) } finally { disposeSpy.mockRestore() } }) - it('never disposes the same ColorAverage instance twice, even in StrictMode', async () => { + it('disposes every ColorAverage instance seen, even across StrictMode\'s mount/cleanup/mount cycle', async () => { const disposedNodes: ColorAverageEffect[] = [] + const seenInstances = new Set() const disposeSpy = vi.spyOn(ColorAverageEffect.prototype, 'dispose').mockImplementation(function ( this: ColorAverageEffect ) { @@ -474,11 +927,17 @@ describe('EffectComposer', () => { ) ) await flush() + if (ref.current) seenInstances.add(ref.current) } await React.act(async () => root.render(null)) - const uniqueDisposed = new Set(disposedNodes) - expect(uniqueDisposed.size).toBe(disposedNodes.length) + // dispose() is idempotent (just event-firing / shallow property + // disposal, no internal state), so StrictMode calling it more than + // once per instance is fine - this only checks nothing leaked. + const disposedSet = new Set(disposedNodes) + for (const instance of seenInstances) { + expect(disposedSet.has(instance)).toBe(true) + } } finally { disposeSpy.mockRestore() } @@ -883,7 +1342,7 @@ describe('EffectComposer', () => { }) describe('performance characteristics (documented, not enforced)', () => { - it('rebuilds the EffectPass once per registration when mounting many effects at once', async () => { + it('rebuilds the EffectPass at most twice when mounting many effects at once', async () => { const addPassSpy = vi.spyOn(EffectComposerImpl.prototype, 'addPass') const ref = React.createRef() @@ -903,9 +1362,43 @@ describe('EffectComposer', () => { const effectPassAddCalls = addPassSpy.mock.calls.filter(([pass]) => pass instanceof EffectPass).length - expect(effectPassAddCalls).toBe(1) + // The node-list change detector and the pass-building effect settle + // over two synchronous layout-effect passes on first mount (detect + // change -> bump a version -> rebuild once more) - a one-time cost, + // not a per-render one. See the "does not rebuild on unrelated + // re-renders" test below for the actual guarantee this trades for. + expect(effectPassAddCalls).toBeLessThanOrEqual(2) + + addPassSpy.mockRestore() + }) + + it('does not rebuild the EffectPass (or re-run EffectPass.initialize) on unrelated re-renders', async () => { + const ref = React.createRef() + + const render = (tick: number) => + root.render( + + + + + ) + + await React.act(async () => render(0)) + await waitForEffects(ref, 1) + + const addPassSpy = vi.spyOn(EffectComposerImpl.prototype, 'addPass') + const initializeSpy = vi.spyOn(EffectPass.prototype, 'initialize') + + for (let t = 1; t <= 5; t++) { + await React.act(async () => render(t)) + await flush() + } + + expect(addPassSpy).not.toHaveBeenCalled() + expect(initializeSpy).not.toHaveBeenCalled() addPassSpy.mockRestore() + initializeSpy.mockRestore() }) }) }) diff --git a/src/tests/EffectGroup.test.tsx b/src/tests/EffectGroup.test.tsx new file mode 100644 index 00000000..e091b9c9 --- /dev/null +++ b/src/tests/EffectGroup.test.tsx @@ -0,0 +1,337 @@ +import { CopyPass, Effect, EffectComposer as EffectComposerImpl, EffectPass, RenderPass } from 'postprocessing' +import * as React from 'react' +import { afterEach, describe, expect, it, vi } from 'vitest' +import { EffectComposer } from '../EffectComposer' +import { EffectGroup } from '../EffectGroup' +import { wrapEffect } from '../wrapEffect' +import { EFFECT_SHADER, flush, root, strict, waitForComposer } from './test-utils' + +class EffectA extends Effect { + constructor() { + super('EffectA', EFFECT_SHADER) + } +} + +class EffectB extends Effect { + constructor() { + super('EffectB', EFFECT_SHADER) + } +} + +class EffectC extends Effect { + constructor() { + super('EffectC', EFFECT_SHADER) + } +} + +const WrappedEffectA = wrapEffect(EffectA) +const WrappedEffectB = wrapEffect(EffectB) +const WrappedEffectC = wrapEffect(EffectC) + +// EffectGroup's pass appears in composer.passes only after its own +// collection-walk effect (child) and EffectComposer's own tree-walk + +// rebuild effects (parent) have all settled - can take more than one flush. +const waitUntil = async (predicate: () => boolean): Promise => { + for (let i = 0; i < 50; i++) { + await flush() + if (predicate()) return + } + throw new Error('Condition never became true') +} + +const waitForGroupPass = async (composer: EffectComposerImpl): Promise => { + await waitUntil(() => composer.passes.some((p) => p instanceof EffectPass)) + return composer.passes.find((p): p is EffectPass => p instanceof EffectPass)! +} + +afterEach(async () => { + await React.act(async () => { + root.render(null) + }) +}) + +describe('EffectGroup', () => { + it('groups its children into exactly one EffectPass, in JSX order', async () => { + const composerRef = React.createRef() + + await React.act(async () => + root.render( + + + + + + + + ) + ) + const composer = await waitForComposer(composerRef) + await waitUntil(() => composer.passes.filter((p) => p instanceof EffectPass).length === 2) + + const effectPasses = composer.passes.filter((p): p is EffectPass => p instanceof EffectPass) + expect(effectPasses).toHaveLength(2) + + const groupPass = effectPasses.find((p) => (p as unknown as { effects: Effect[] }).effects.length === 2)! + expect(groupPass).toBeDefined() + const groupedEffects = (groupPass as unknown as { effects: Effect[] }).effects + expect(groupedEffects[0]).toBeInstanceOf(EffectB) + expect(groupedEffects[1]).toBeInstanceOf(EffectC) + }) + + it('keeps sibling effects out of the group and preserves relative pass order', async () => { + const composerRef = React.createRef() + + await React.act(async () => + root.render( + + + + + + + + ) + ) + const composer = await waitForComposer(composerRef) + await waitUntil(() => composer.passes.filter((p) => p instanceof EffectPass).length === 3) + + // A and C are not adjacent (the group's pass sits between them), so + // they must NOT get merged into one EffectPass with each other. + expect(composer.passes[0]).toBeInstanceOf(RenderPass) + + const effectPasses = composer.passes.filter((p): p is EffectPass => p instanceof EffectPass) + expect(effectPasses).toHaveLength(3) + + const effectsOf = (p: EffectPass) => (p as unknown as { effects: Effect[] }).effects + expect(effectsOf(effectPasses[0])).toEqual([expect.any(EffectA)]) + expect(effectsOf(effectPasses[1])).toEqual([expect.any(EffectB)]) + expect(effectsOf(effectPasses[2])).toEqual([expect.any(EffectC)]) + }) + + it('toggles enabled without reconstructing the pass', async () => { + const composerRef = React.createRef() + const groupRef = React.createRef() + + const render = (enabled: boolean) => + root.render( + + + + + + ) + + await React.act(async () => render(true)) + await waitForComposer(composerRef) + await waitUntil(() => groupRef.current !== null) + + const pass = groupRef.current + expect(pass).not.toBeNull() + expect(pass!.enabled).toBe(true) + + await React.act(async () => render(false)) + await flush() + + expect(groupRef.current).toBe(pass) + expect(pass!.enabled).toBe(false) + }) + + it('keeps something rendering to the screen when the only effect is disabled, via a trailing CopyPass', async () => { + const composerRef = React.createRef() + const groupRef = React.createRef() + + const render = (enabled: boolean) => + root.render( + + + + + + ) + + await React.act(async () => render(true)) + const composer = await waitForComposer(composerRef) + await waitUntil(() => groupRef.current !== null) + + // Exactly one currently-enabled pass must own renderToScreen at all + // times - otherwise nothing (enabled) ever reaches the canvas. + const enabledRenderToScreenCount = () => composer.passes.filter((p) => p.enabled && p.renderToScreen).length + + // Enabled: the group's own EffectPass runs; the trailing CopyPass + // (always enabled, always structurally last) owns renderToScreen. + expect(groupRef.current!.enabled).toBe(true) + expect(groupRef.current!.renderToScreen).toBe(false) + expect(enabledRenderToScreenCount()).toBe(1) + + await React.act(async () => render(false)) + await flush() + + // Disabled: the render loop skips the EffectPass entirely, but the + // CopyPass - unaffected, still enabled, still owning renderToScreen - + // blits whatever buffer is already there through to the screen. + expect(groupRef.current!.enabled).toBe(false) + expect(enabledRenderToScreenCount()).toBe(1) + }) + + it('shares a single trailing CopyPass across multiple EffectGroups, not one per group', async () => { + const composerRef = React.createRef() + + await React.act(async () => + root.render( + + + + + + + + + ) + ) + const composer = await waitForComposer(composerRef) + await waitUntil(() => composer.passes.filter((p) => p instanceof EffectPass).length === 2) + + const copyPasses = composer.passes.filter((p) => p instanceof CopyPass) + expect(copyPasses).toHaveLength(1) + // Structurally last, so it - not either group's own pass - owns renderToScreen. + expect(composer.passes[composer.passes.length - 1]).toBe(copyPasses[0]) + expect(copyPasses[0].renderToScreen).toBe(true) + }) + + it('adds no CopyPass at all when no EffectGroup is present', async () => { + const composerRef = React.createRef() + + await React.act(async () => + root.render( + + + + ) + ) + const composer = await waitForComposer(composerRef) + await waitUntil(() => composer.passes.filter((p) => p instanceof EffectPass).length === 1) + + expect(composer.passes.some((p) => p instanceof CopyPass)).toBe(false) + }) + + it('exposes the underlying EffectPass via ref', async () => { + const composerRef = React.createRef() + const groupRef = React.createRef() + + await React.act(async () => + root.render( + + + + + + ) + ) + const composer = await waitForComposer(composerRef) + await waitForGroupPass(composer) + + expect(groupRef.current).toBeInstanceOf(EffectPass) + expect(composer.passes).toContain(groupRef.current) + }) + + it('disposes its pass on unmount without double-disposing the grouped effects', async () => { + const composerRef = React.createRef() + const effectDisposeSpy = vi.spyOn(EffectB.prototype, 'dispose') + + await React.act(async () => + root.render( + + + + + + ) + ) + const composer = await waitForComposer(composerRef) + const groupPass = await waitForGroupPass(composer) + expect(groupPass).toBeDefined() + + await React.act(async () => root.render(null)) + + expect(effectDisposeSpy).toHaveBeenCalledTimes(1) + + effectDisposeSpy.mockRestore() + }) + + it('rebuilds the pass (new identity) when the effect list inside the group changes, mirroring buildPasses', async () => { + const composerRef = React.createRef() + const groupRef = React.createRef() + + const render = (withSecond: boolean) => + root.render( + + + + {withSecond && } + + + ) + + await React.act(async () => render(false)) + await waitForComposer(composerRef) + await waitUntil(() => groupRef.current !== null) + + const firstPass = groupRef.current + expect((firstPass as unknown as { effects: Effect[] }).effects).toHaveLength(1) + + await React.act(async () => render(true)) + await waitUntil(() => groupRef.current !== firstPass) + + expect(groupRef.current).not.toBe(firstPass) + expect((groupRef.current as unknown as { effects: Effect[] }).effects).toHaveLength(2) + }) + + it('initializes every effect in a rebuilt group, including ones added at runtime', async () => { + const composerRef = React.createRef() + const groupRef = React.createRef() + const initializeSpy = vi.spyOn(EffectB.prototype, 'initialize') + + const render = (withSecond: boolean) => + root.render( + + + + {withSecond && } + + + ) + + await React.act(async () => render(false)) + await waitForComposer(composerRef) + await waitUntil(() => groupRef.current !== null) + + expect(initializeSpy).not.toHaveBeenCalled() + + await React.act(async () => render(true)) + await waitUntil(() => (groupRef.current as unknown as { effects: Effect[] }).effects.length === 2) + + expect(initializeSpy).toHaveBeenCalledTimes(1) + + initializeSpy.mockRestore() + }) + + it('ends up with exactly one EffectPass in StrictMode, not a stray extra from double-invoked state updates', async () => { + const composerRef = React.createRef() + + await React.act(async () => + root.render( + strict( + + + + + + ) + ) + ) + const composer = await waitForComposer(composerRef) + await waitUntil(() => composer.passes.some((p) => p instanceof EffectPass)) + + expect(composer.passes.filter((p) => p instanceof EffectPass)).toHaveLength(1) + }) +}) diff --git a/src/tests/Glitch.test.tsx b/src/tests/Glitch.test.tsx new file mode 100644 index 00000000..c383cb4c --- /dev/null +++ b/src/tests/Glitch.test.tsx @@ -0,0 +1,56 @@ +import { EffectComposer as EffectComposerImpl, GlitchEffect, GlitchMode } from 'postprocessing' +import * as React from 'react' +import { describe, expect, it } from 'vitest' +import { EffectComposer } from '../EffectComposer' +import { Glitch } from '../effects/Glitch' +import { flush, root } from './test-utils' + +describe('Glitch', () => { + it('toggles active/mode live, without reconstructing the effect', async () => { + const composerRef = React.createRef() + const ref = React.createRef() + + const render = (active: boolean) => + root.render( + + + + ) + + await React.act(async () => render(true)) + await flush() + const first = ref.current + expect(first!.mode).toBe(GlitchMode.SPORADIC) + + await React.act(async () => render(false)) + await flush() + + expect(ref.current).toBe(first) + expect(ref.current!.mode).toBe(GlitchMode.DISABLED) + + await React.act(async () => root.render(null)) + }) + + it('reconstructs when dtSize (construction-only) changes', async () => { + const composerRef = React.createRef() + const ref = React.createRef() + + const render = (dtSize: number) => + root.render( + + + + ) + + await React.act(async () => render(64)) + await flush() + const first = ref.current + + await React.act(async () => render(128)) + await flush() + + expect(ref.current).not.toBe(first) + + await React.act(async () => root.render(null)) + }) +}) diff --git a/src/tests/GodRays.test.tsx b/src/tests/GodRays.test.tsx new file mode 100644 index 00000000..b0a34f29 --- /dev/null +++ b/src/tests/GodRays.test.tsx @@ -0,0 +1,101 @@ +import { EffectComposer as EffectComposerImpl, GodRaysEffect } from 'postprocessing' +import * as React from 'react' +import { Mesh, SphereGeometry } from 'three' +import { describe, expect, it, vi } from 'vitest' +import { EffectComposer } from '../EffectComposer' +import { GodRays } from '../effects/GodRays' +import { flush, root, waitForComposer } from './test-utils' + +describe('GodRays', () => { + it('applies density live, without reconstructing the effect', async () => { + const composerRef = React.createRef() + const ref = React.createRef() + const sun = new Mesh(new SphereGeometry(1, 8, 8)) + + const render = (density: number) => + root.render( + + + + + ) + + await React.act(async () => render(0.9)) + await waitForComposer(composerRef) + await flush() + const first = ref.current + expect(first!.godRaysMaterial.density).toBeCloseTo(0.9) + + await React.act(async () => render(0.5)) + await flush() + + expect(ref.current).toBe(first) + expect(ref.current!.godRaysMaterial.density).toBeCloseTo(0.5) + + await React.act(async () => root.render(null)) + }) + + it('still reconstructs when resolutionScale (construction-only) changes', async () => { + const composerRef = React.createRef() + const ref = React.createRef() + const sun = new Mesh(new SphereGeometry(1, 8, 8)) + + const render = (resolutionScale: number) => + root.render( + + + + + ) + + await React.act(async () => render(0.5)) + await waitForComposer(composerRef) + await flush() + const first = ref.current + + await React.act(async () => render(1)) + await flush() + + expect(ref.current).not.toBe(first) + + await React.act(async () => root.render(null)) + }) + + it('invalidates when sun is swapped for a different mesh, so frameloop="demand" repaints', async () => { + const composerRef = React.createRef() + const ref = React.createRef() + const sunA = new Mesh(new SphereGeometry(1, 8, 8)) + const sunB = new Mesh(new SphereGeometry(1, 8, 8)) + + // Both meshes are mounted unconditionally throughout - only the `sun` + // prop GodRays points at changes, so the only invalidate() candidate is + // GodRays.tsx's own effect.lightSource assignment, not r3f's native + // handling of a swap (a real prop change it + // already invalidates for on its own, which a naive test could + // mistake for this effect's own behavior). + const render = (sun: Mesh) => + root.render( + + + + + + ) + + await React.act(async () => render(sunA)) + await waitForComposer(composerRef) + await flush() + expect(ref.current!.lightSource).toBe(sunA) + + const invalidateSpy = vi.spyOn(root.render(null).getState(), 'invalidate') + + await React.act(async () => render(sunB)) + await flush() + + expect(ref.current!.lightSource).toBe(sunB) + expect(invalidateSpy).toHaveBeenCalled() + + invalidateSpy.mockRestore() + await React.act(async () => root.render(null)) + }) +}) diff --git a/src/tests/Grid.test.tsx b/src/tests/Grid.test.tsx new file mode 100644 index 00000000..60326989 --- /dev/null +++ b/src/tests/Grid.test.tsx @@ -0,0 +1,34 @@ +import { EffectComposer as EffectComposerImpl, GridEffect } from 'postprocessing' +import * as React from 'react' +import { describe, expect, it } from 'vitest' +import { EffectComposer } from '../EffectComposer' +import { Grid } from '../effects/Grid' +import { flush, root } from './test-utils' + +describe('Grid', () => { + it('applies scale/lineWidth live, without reconstructing the effect', async () => { + const composerRef = React.createRef() + const ref = React.createRef() + + const render = (scale: number) => + root.render( + + + + ) + + await React.act(async () => render(1)) + await flush() + const first = ref.current + expect(first!.scale).toBe(1) + expect(first!.lineWidth).toBeCloseTo(0.1) + + await React.act(async () => render(2)) + await flush() + + expect(ref.current).toBe(first) + expect(ref.current!.scale).toBe(2) + + await React.act(async () => root.render(null)) + }) +}) diff --git a/src/tests/LUT.test.tsx b/src/tests/LUT.test.tsx new file mode 100644 index 00000000..28a2ce2d --- /dev/null +++ b/src/tests/LUT.test.tsx @@ -0,0 +1,64 @@ +import { EffectComposer as EffectComposerImpl, LUT3DEffect } from 'postprocessing' +import * as React from 'react' +import { DataTexture } from 'three' +import { describe, expect, it } from 'vitest' +import { EffectComposer } from '../EffectComposer' +import { LUT } from '../effects/LUT' +import { flush, root, waitForComposer } from './test-utils' + +describe('LUT', () => { + it('applies tetrahedralInterpolation live, without reconstructing the effect', async () => { + const composerRef = React.createRef() + const ref = React.createRef() + const lut = new DataTexture(new Uint8Array(4 * 4 * 4 * 4), 4, 4) + + const render = (tetrahedralInterpolation: boolean) => + root.render( + + + + ) + + await React.act(async () => render(false)) + await waitForComposer(composerRef) + await flush() + const first = ref.current + expect(first!.tetrahedralInterpolation).toBe(false) + + await React.act(async () => render(true)) + await flush() + + expect(ref.current).toBe(first) + expect(ref.current!.tetrahedralInterpolation).toBe(true) + + await React.act(async () => root.render(null)) + }) + + it('applies a new lut live via its own setter, without reconstructing the effect', async () => { + const composerRef = React.createRef() + const ref = React.createRef() + const lutA = new DataTexture(new Uint8Array(4 * 4 * 4 * 4), 4, 4) + const lutB = new DataTexture(new Uint8Array(4 * 4 * 4 * 4), 4, 4) + + const render = (lut: DataTexture) => + root.render( + + + + ) + + await React.act(async () => render(lutA)) + await waitForComposer(composerRef) + await flush() + const first = ref.current + expect(first!.lut).toBe(lutA) + + await React.act(async () => render(lutB)) + await flush() + + expect(ref.current).toBe(first) + expect(ref.current!.lut).toBe(lutB) + + await React.act(async () => root.render(null)) + }) +}) diff --git a/src/tests/N8AO.test.tsx b/src/tests/N8AO.test.tsx new file mode 100644 index 00000000..447dc7b0 --- /dev/null +++ b/src/tests/N8AO.test.tsx @@ -0,0 +1,92 @@ +import { CopyPass, EffectComposer as EffectComposerImpl } from 'postprocessing' +import * as React from 'react' +import { describe, expect, it, vi } from 'vitest' +import { EffectComposer } from '../EffectComposer' +import { N8AO } from '../passes/N8AO' +import { flush, root, waitForComposer } from './test-utils' + +describe('N8AO', () => { + it('invalidates after a live config change and after a quality change, so frameloop="demand" repaints', async () => { + const composerRef = React.createRef() + const invalidateSpy = vi.spyOn(root.render(null).getState(), 'invalidate') + + const render = (intensity: number, quality?: 'performance' | 'ultra') => + root.render( + + + + ) + + await React.act(async () => render(1)) + await flush() + invalidateSpy.mockClear() + + await React.act(async () => render(2)) + await flush() + expect(invalidateSpy).toHaveBeenCalled() + + invalidateSpy.mockClear() + + await React.act(async () => render(2, 'ultra')) + await flush() + expect(invalidateSpy).toHaveBeenCalled() + + invalidateSpy.mockRestore() + await React.act(async () => root.render(null)) + }) + + it('toggles enabled without reconstructing the pass', async () => { + const ref = React.createRef() + + const render = (enabled: boolean) => + root.render( + + + + ) + + await React.act(async () => render(true)) + await flush() + const instance = ref.current + expect(instance.enabled).toBe(true) + + await React.act(async () => render(false)) + await flush() + expect(ref.current).toBe(instance) + expect(instance.enabled).toBe(false) + + await React.act(async () => root.render(null)) + }) + + it('gets the shared trailing CopyPass safety net, keeping something rendering to screen when disabled', async () => { + const composerRef = React.createRef() + const ref = React.createRef() + + const render = (enabled: boolean) => + root.render( + + + + ) + + await React.act(async () => render(true)) + const composer = await waitForComposer(composerRef) + await flush() + + const copyPass = composer.passes.find((p) => p instanceof CopyPass) + expect(copyPass).toBeDefined() + + const enabledRenderToScreenCount = () => composer.passes.filter((p) => p.enabled && p.renderToScreen).length + expect(enabledRenderToScreenCount()).toBe(1) + + await React.act(async () => render(false)) + await flush() + + // N8AO's own pass is now disabled, but exactly one enabled pass must + // still own renderToScreen - otherwise nothing reaches the canvas. + expect(enabledRenderToScreenCount()).toBe(1) + expect(copyPass!.renderToScreen).toBe(true) + + await React.act(async () => root.render(null)) + }) +}) diff --git a/src/tests/Outline.test.tsx b/src/tests/Outline.test.tsx index e0e62062..54f755f5 100644 --- a/src/tests/Outline.test.tsx +++ b/src/tests/Outline.test.tsx @@ -86,4 +86,126 @@ describe('Outline', () => { await waitForComposer(composerRef) await expect(flush()).resolves.not.toThrow() }) + + it('applies visibleEdgeColor live, without reconstructing the effect (#143)', async () => { + const composerRef = React.createRef() + const effectRef = React.createRef() + + const render = (color: number) => + root.render( + + + + ) + + await React.act(async () => render(0xff0000)) + await waitForComposer(composerRef) + await flush() + + const first = effectRef.current + expect(first!.visibleEdgeColor.getHex()).toBe(0xff0000) + + await React.act(async () => render(0x00ff00)) + await flush() + + expect(effectRef.current).toBe(first) + expect(effectRef.current!.visibleEdgeColor.getHex()).toBe(0x00ff00) + }) + + it('accepts a CSS color string for visibleEdgeColor/hiddenEdgeColor, not just a numeric hex (#187, #182)', async () => { + const composerRef = React.createRef() + const effectRef = React.createRef() + + await React.act(async () => + root.render( + + + + ) + ) + await waitForComposer(composerRef) + await flush() + + expect(effectRef.current!.visibleEdgeColor.getHex()).toBe(0xff0000) + expect(effectRef.current!.hiddenEdgeColor.getHex()).toBe(0x0000ff) + }) + + it('resets edgeStrength to its constructor default when the prop is removed', async () => { + const composerRef = React.createRef() + const effectRef = React.createRef() + + await React.act(async () => + root.render( + + + + ) + ) + await waitForComposer(composerRef) + await flush() + + expect(effectRef.current!.edgeStrength).toBe(100) + + await React.act(async () => + root.render( + + + + ) + ) + await flush() + + expect(effectRef.current!.edgeStrength).toBe(1) + }) + + it('still reconstructs when a construction-only prop (resolutionScale) changes', async () => { + const composerRef = React.createRef() + const effectRef = React.createRef() + + const render = (resolutionScale: number) => + root.render( + + + + ) + + await React.act(async () => render(0.5)) + await waitForComposer(composerRef) + await flush() + const first = effectRef.current + + await React.act(async () => render(1)) + await flush() + + expect(effectRef.current).not.toBe(first) + }) + + it('does not dispose its render target on unrelated re-renders (multisampling has an unconditional dispose side effect)', async () => { + const composerRef = React.createRef() + const effectRef = React.createRef() + + const render = (tick: number) => + root.render( + + + + + ) + + await React.act(async () => render(0)) + await waitForComposer(composerRef) + await flush() + + // @ts-expect-error - `renderTargetMask` isn't part of the public OutlineEffect typing + const disposeSpy = vi.spyOn(effectRef.current!.renderTargetMask, 'dispose') + + for (let t = 1; t <= 5; t++) { + await React.act(async () => render(t)) + await flush() + } + + expect(disposeSpy).not.toHaveBeenCalled() + disposeSpy.mockRestore() + }) + }) diff --git a/src/tests/SSAO.test.tsx b/src/tests/SSAO.test.tsx new file mode 100644 index 00000000..694d2027 --- /dev/null +++ b/src/tests/SSAO.test.tsx @@ -0,0 +1,140 @@ +import { EffectComposer as EffectComposerImpl, SSAOEffect } from 'postprocessing' +import * as React from 'react' +import { Color } from 'three' +import { describe, expect, it } from 'vitest' +import { EffectComposer } from '../EffectComposer' +import { SSAO } from '../effects/SSAO' +import { flush, root, waitForComposer } from './test-utils' + +describe('SSAO', () => { + it('resets color/fade/minRadiusScale to their constructor defaults when removed, not the first-mounted value', async () => { + const composerRef = React.createRef() + const ref = React.createRef() + + const render = (withOverrides: boolean) => + root.render( + + + + ) + + await React.act(async () => render(true)) + await waitForComposer(composerRef) + await flush() + + expect(ref.current!.color!.getHexString()).toBe('ff0000') + expect(ref.current!.ssaoMaterial.fade).toBeCloseTo(0.5) + expect(ref.current!.ssaoMaterial.minRadiusScale).toBeCloseTo(0.9) + + await React.act(async () => render(false)) + await flush() + + // SSAOEffect's own constructor defaults (null / 0.01 / 0.1), not the + // values from the first render this instance ever saw. + expect(ref.current!.color).toBeNull() + expect(ref.current!.ssaoMaterial.fade).toBeCloseTo(0.01) + expect(ref.current!.ssaoMaterial.minRadiusScale).toBeCloseTo(0.1) + }) + + it('applies intensity live, without reconstructing the effect', async () => { + const composerRef = React.createRef() + const ref = React.createRef() + + const render = (intensity: number) => + root.render( + + + + ) + + await React.act(async () => render(1)) + await waitForComposer(composerRef) + await flush() + const first = ref.current + expect(first!.intensity).toBe(1) + + await React.act(async () => render(2)) + await flush() + + expect(ref.current).toBe(first) + expect(ref.current!.intensity).toBe(2) + + await React.act(async () => root.render(null)) + }) + + it('applies bias live via the nested ssaoMaterial, without reconstructing the effect', async () => { + const composerRef = React.createRef() + const ref = React.createRef() + + const render = (bias: number) => + root.render( + + + + ) + + await React.act(async () => render(0.5)) + await waitForComposer(composerRef) + await flush() + const first = ref.current + expect(first!.ssaoMaterial.bias).toBeCloseTo(0.5) + + await React.act(async () => render(0.8)) + await flush() + + expect(ref.current).toBe(first) + expect(ref.current!.ssaoMaterial.bias).toBeCloseTo(0.8) + + await React.act(async () => root.render(null)) + }) + + it('applies worldProximityThreshold live via the nested ssaoMaterial, without reconstructing the effect', async () => { + const composerRef = React.createRef() + const ref = React.createRef() + + const render = (worldProximityThreshold: number) => + root.render( + + + + ) + + await React.act(async () => render(0.5)) + await waitForComposer(composerRef) + await flush() + const first = ref.current + expect(first!.ssaoMaterial.worldProximityThreshold).toBeCloseTo(0.5) + + await React.act(async () => render(0.8)) + await flush() + + expect(ref.current).toBe(first) + expect(ref.current!.ssaoMaterial.worldProximityThreshold).toBeCloseTo(0.8) + + await React.act(async () => root.render(null)) + }) + + it('still reconstructs when resolutionScale (construction-only) changes', async () => { + const composerRef = React.createRef() + const ref = React.createRef() + + const render = (resolutionScale: number) => + root.render( + + + + ) + + await React.act(async () => render(0.5)) + await waitForComposer(composerRef) + await flush() + const first = ref.current + + await React.act(async () => render(1)) + await flush() + + expect(ref.current).not.toBe(first) + + await React.act(async () => root.render(null)) + }) +}) diff --git a/src/tests/SelectiveBloom.test.tsx b/src/tests/SelectiveBloom.test.tsx index d7bff48e..41a89f62 100644 --- a/src/tests/SelectiveBloom.test.tsx +++ b/src/tests/SelectiveBloom.test.tsx @@ -115,4 +115,52 @@ describe('SelectiveBloom', () => { await waitForComposer(composerRef) await expect(flush()).resolves.not.toThrow() }) + + it('applies intensity live, without reconstructing the effect', async () => { + const composerRef = React.createRef() + const effectRef = React.createRef() + const light = new PointLight() + + const render = (intensity: number) => + root.render( + + + + ) + + await React.act(async () => render(1)) + await waitForComposer(composerRef) + await flush() + const first = effectRef.current + expect(first!.intensity).toBe(1) + + await React.act(async () => render(3)) + await flush() + + expect(effectRef.current).toBe(first) + expect(effectRef.current!.intensity).toBe(3) + }) + + it('still reconstructs when luminanceThreshold changes (no live setter in postprocessing)', async () => { + const composerRef = React.createRef() + const effectRef = React.createRef() + const light = new PointLight() + + const render = (luminanceThreshold: number) => + root.render( + + + + ) + + await React.act(async () => render(0.5)) + await waitForComposer(composerRef) + await flush() + const first = effectRef.current + + await React.act(async () => render(0.8)) + await flush() + + expect(effectRef.current).not.toBe(first) + }) }) diff --git a/src/tests/ShockWave.test.tsx b/src/tests/ShockWave.test.tsx new file mode 100644 index 00000000..3123aae3 --- /dev/null +++ b/src/tests/ShockWave.test.tsx @@ -0,0 +1,95 @@ +import { EffectComposer as EffectComposerImpl, ShockWaveEffect } from 'postprocessing' +import * as React from 'react' +import { Vector3 } from 'three' +import { describe, expect, it } from 'vitest' +import { EffectComposer } from '../EffectComposer' +import { ShockWave } from '../effects/ShockWave' +import { flush, root } from './test-utils' + +describe('ShockWave', () => { + it('applies speed and position, which createEffectComponent cannot (ShockWaveEffect takes them as a 3rd ctor arg)', async () => { + const ref = React.createRef() + const composerRef = React.createRef() + const position = new Vector3(1, 2, 3) + + await React.act(async () => + root.render( + + + + ) + ) + + await flush() + + expect(ref.current!.speed).toBe(5) + expect(ref.current!.position).toBe(position) + + await React.act(async () => root.render(null)) + }) + + it('updates speed/position live, without reconstructing the instance', async () => { + const ref = React.createRef() + const composerRef = React.createRef() + + await React.act(async () => + root.render( + + + + ) + ) + + await flush() + + const firstInstance = ref.current + + await React.act(async () => + root.render( + + + + ) + ) + + await flush() + + expect(ref.current).toBe(firstInstance) + expect(ref.current!.speed).toBe(2) + expect(ref.current!.waveSize).toBe(0.5) + + await React.act(async () => root.render(null)) + }) + + it('resets speed to its constructor default when the prop is removed', async () => { + const ref = React.createRef() + const composerRef = React.createRef() + + await React.act(async () => + root.render( + + + + ) + ) + + await flush() + + expect(ref.current!.speed).toBe(5) + const defaultSpeed = 2 + + await React.act(async () => + root.render( + + + + ) + ) + + await flush() + + expect(ref.current!.speed).toBe(defaultSpeed) + + await React.act(async () => root.render(null)) + }) +}) diff --git a/src/tests/TiltShift.test.tsx b/src/tests/TiltShift.test.tsx new file mode 100644 index 00000000..4979f55f --- /dev/null +++ b/src/tests/TiltShift.test.tsx @@ -0,0 +1,76 @@ +import { EffectComposer as EffectComposerImpl, TiltShiftEffect } from 'postprocessing' +import * as React from 'react' +import { describe, expect, it } from 'vitest' +import { EffectComposer } from '../EffectComposer' +import { TiltShift } from '../effects/TiltShift' +import { flush, root } from './test-utils' + +describe('TiltShift', () => { + it('applies resolutionScale at construction (previously never reached the effect at all)', async () => { + const composerRef = React.createRef() + const ref = React.createRef() + + await React.act(async () => + root.render( + + + + ) + ) + + await flush() + + expect(ref.current!.blurPass.resolution.scale).toBeCloseTo(0.25) + + await React.act(async () => root.render(null)) + }) + + it('reconstructs when resolutionScale (construction-only) changes', async () => { + const composerRef = React.createRef() + const ref = React.createRef() + + const render = (resolutionScale: number) => + root.render( + + + + ) + + await React.act(async () => render(0.25)) + await flush() + const first = ref.current + + await React.act(async () => render(0.5)) + await flush() + + expect(ref.current).not.toBe(first) + expect(ref.current!.blurPass.resolution.scale).toBeCloseTo(0.5) + + await React.act(async () => root.render(null)) + }) + + it('applies offset live, without reconstructing the effect', async () => { + const composerRef = React.createRef() + const ref = React.createRef() + + const render = (offset: number) => + root.render( + + + + ) + + await React.act(async () => render(0.1)) + await flush() + const first = ref.current + expect(first!.offset).toBeCloseTo(0.1) + + await React.act(async () => render(0.2)) + await flush() + + expect(ref.current).toBe(first) + expect(ref.current!.offset).toBeCloseTo(0.2) + + await React.act(async () => root.render(null)) + }) +}) diff --git a/src/tests/createEffectComponent.test.tsx b/src/tests/createEffectComponent.test.tsx new file mode 100644 index 00000000..a6b2d49b --- /dev/null +++ b/src/tests/createEffectComponent.test.tsx @@ -0,0 +1,286 @@ +import { Effect, EffectComposer as EffectComposerImpl } from 'postprocessing' +import * as React from 'react' +import { Uniform } from 'three' +import { describe, expect, it, vi } from 'vitest' +import { createEffectComponent } from '../createEffectComponent' +import { EffectComposer } from '../EffectComposer' +import { flush, root } from './test-utils' + +// Zero-arity, real accessor - the class of effect createEffectComponent +// targets. Matches BloomEffect's shape: `constructor({...} = {})`. +class FakeEffect extends Effect { + private _value: number + constructor({ value = 0 }: { value?: number } = {}) { + super('FakeEffect', 'mainImage() {}') + this._value = value + } + get value() { + return this._value + } + set value(v: number) { + this._value = v + } +} + +const FakeEffectComponent = /* @__PURE__ */ createEffectComponent(FakeEffect) + +// Options stored only in `uniforms` (mirrors WaterEffectImpl/RampEffect +// before this branch gave them real accessors) - `factor` here HAS a real +// accessor, proving that's what makes a plain JSX prop actually reach it. +class UniformFixtureEffect extends Effect { + constructor({ factor = 0 }: { factor?: number } = {}) { + super('UniformFixtureEffect', 'mainImage() {}', { uniforms: new Map([['factor', new Uniform(factor)]]) }) + } + get factor(): number { + return this.uniforms.get('factor')!.value + } + set factor(v: number) { + this.uniforms.get('factor')!.value = v + } +} + +const UniformFixtureComponent = /* @__PURE__ */ createEffectComponent( + UniformFixtureEffect +) + +describe('createEffectComponent', () => { + it('constructs the effect and passes props through to the instance', async () => { + const composerRef = React.createRef() + + await React.act(async () => + root.render( + + + + ) + ) + + await flush() + + // @ts-expect-error - `effects` isn't part of the public Pass typing + const effect = composerRef.current!.passes[1].effects[0] + + expect(effect).toBeInstanceOf(FakeEffect) + expect(effect.value).toBe(42) + + await React.act(async () => root.render(null)) + }) + + it('applies a live prop without reconstructing the instance (r3f-native, no args change)', async () => { + const composerRef = React.createRef() + const ref = React.createRef() + + const render = (value: number) => + root.render( + + + + ) + + await React.act(async () => render(1)) + await flush() + const first = ref.current + expect(first!.value).toBe(1) + + await React.act(async () => render(2)) + await flush() + + expect(ref.current).toBe(first) + expect(ref.current!.value).toBe(2) + + await React.act(async () => root.render(null)) + }) + + it('resets a live prop to its constructor default when removed (r3f-native diffProps)', async () => { + const composerRef = React.createRef() + const ref = React.createRef() + + await React.act(async () => + root.render( + + + + ) + ) + await flush() + expect(ref.current!.value).toBe(5) + + await React.act(async () => + root.render( + + + + ) + ) + await flush() + + expect(ref.current!.value).toBe(0) + + await React.act(async () => root.render(null)) + }) + + it('reconstructs when an explicit args prop changes, same as any other r3f element', async () => { + const composerRef = React.createRef() + const ref = React.createRef() + + const render = (value: number) => + root.render( + + + + ) + + await React.act(async () => render(1)) + await flush() + const first = ref.current + expect(first!.value).toBe(1) + + await React.act(async () => render(2)) + await flush() + + expect(ref.current).not.toBe(first) + expect(ref.current!.value).toBe(2) + + await React.act(async () => root.render(null)) + }) + + it('applies blendFunction/opacity through blendMode, not as a stray top-level property', async () => { + const composerRef = React.createRef() + const ref = React.createRef() + + await React.act(async () => + root.render( + + + + ) + ) + await flush() + + expect(ref.current!.blendMode.blendFunction).toBe(7) + expect(ref.current!.blendMode.opacity.value).toBe(0.5) + expect((ref.current as unknown as { blendFunction?: unknown }).blendFunction).toBeUndefined() + + await React.act(async () => root.render(null)) + }) + + it('resets blendFunction/opacity to blendMode\'s own defaults when the props are removed', async () => { + const composerRef = React.createRef() + const ref = React.createRef() + + await React.act(async () => + root.render( + + + + ) + ) + await flush() + const defaultBlendFunction = ref.current!.blendMode.blendFunction + const defaultOpacity = ref.current!.blendMode.opacity.value + + await React.act(async () => + root.render( + + + + ) + ) + await flush() + expect(ref.current!.blendMode.blendFunction).toBe(7) + expect(ref.current!.blendMode.opacity.value).toBe(0.5) + + await React.act(async () => + root.render( + + + + ) + ) + await flush() + + expect(ref.current!.blendMode.blendFunction).toBe(defaultBlendFunction) + expect(ref.current!.blendMode.opacity.value).toBe(defaultOpacity) + + await React.act(async () => root.render(null)) + }) + + it('updates a uniforms-Map-backed prop live via its accessor, without reconstructing', async () => { + const composerRef = React.createRef() + const ref = React.createRef() + + const render = (factor: number) => + root.render( + + + + ) + + await React.act(async () => render(1)) + await flush() + const first = ref.current + expect(first!.uniforms.get('factor')!.value).toBe(1) + + await React.act(async () => render(2)) + await flush() + + expect(ref.current).toBe(first) + expect(ref.current!.uniforms.get('factor')!.value).toBe(2) + + await React.act(async () => root.render(null)) + }) + + it('disposes the instance on unmount', async () => { + const disposeSpy = vi.spyOn(FakeEffect.prototype, 'dispose') + const composerRef = React.createRef() + + try { + await React.act(async () => + root.render( + + + + ) + ) + await flush() + + await React.act(async () => root.render(null)) + + expect(disposeSpy).toHaveBeenCalledTimes(1) + } finally { + disposeSpy.mockRestore() + } + }) + + it('forwards a callback ref\'s own returned cleanup (React 19 ref cleanup), instead of dropping it', async () => { + const composerRef = React.createRef() + const events: string[] = [] + const cleanup = vi.fn(() => { + events.push('cleanup') + }) + const callbackRef = vi.fn((instance: FakeEffect | null) => { + events.push(instance ? 'attach' : 'attach-null') + if (instance) return cleanup + }) + + await React.act(async () => + root.render( + + + + ) + ) + await flush() + + expect(callbackRef).toHaveBeenCalledTimes(1) + expect(cleanup).not.toHaveBeenCalled() + + await React.act(async () => root.render(null)) + + // React 19 ref-cleanup semantics: once a cleanup is returned, it's + // called directly - the callback itself is never re-invoked with null. + expect(cleanup).toHaveBeenCalledTimes(1) + expect(callbackRef).toHaveBeenCalledTimes(1) + expect(events).toEqual(['attach', 'cleanup']) + }) +}) diff --git a/src/tests/effects.smoke.test.tsx b/src/tests/effects.smoke.test.tsx index f2b38d1f..1d098f80 100644 --- a/src/tests/effects.smoke.test.tsx +++ b/src/tests/effects.smoke.test.tsx @@ -10,8 +10,8 @@ // effects still need manual/visual verification before release. // // Coverage is enforced by the last test in this file: every *.tsx file in -// src/effects must appear either in SMOKE_CASES or EXCLUDED below. Adding a -// new effect file without touching either list fails CI. +// src/effects or src/passes must appear either in SMOKE_CASES or EXCLUDED +// below. Adding a new effect/pass file without touching either list fails CI. // // This file is excluded from `tsc -p tsconfig.json` (it matches // src/**/*.test.*), so editors fall back to a detached/inferred compilation @@ -23,7 +23,7 @@ import fs from 'node:fs' import path from 'node:path' import { fileURLToPath } from 'node:url' -import { CopyPass, DepthPickingPass, EffectComposer as EffectComposerImpl } from 'postprocessing' +import { DepthPickingPass as DepthPickingPassImpl, EffectComposer as EffectComposerImpl } from 'postprocessing' import * as React from 'react' import * as THREE from 'three' import { describe, expect, it, vi } from 'vitest' @@ -44,7 +44,6 @@ import { Grid } from '../effects/Grid' import { HueSaturation } from '../effects/HueSaturation' import { LensFlare } from '../effects/LensFlare' import { LUT } from '../effects/LUT' -import { N8AO } from '../effects/N8AO' import { Noise } from '../effects/Noise' import { Outline } from '../effects/Outline' import { Pixelation } from '../effects/Pixelation' @@ -60,16 +59,18 @@ import { TiltShift2 } from '../effects/TiltShift2' import { ToneMapping } from '../effects/ToneMapping' import { Vignette } from '../effects/Vignette' import { WaterEffect } from '../effects/Water' +import { DepthPicking } from '../passes/DepthPicking' +import { N8AO } from '../passes/N8AO' import { flush, root } from './test-utils' type SmokeCase = { - /** Filename under src/effects this case covers — drives the coverage check below. */ + /** Filename under src/effects or src/passes this case covers — drives the coverage check below. */ file: string label: string composerProps?: Record /** Extra scene content the effect needs (e.g. a sun mesh for GodRays). */ extras?: React.ReactNode - /** Renders the effect element. `ref` may be ignored by effects that don't forward one (e.g. LensFlare). */ + /** Renders the effect element. */ effect: (ref: React.Ref) => React.ReactElement } @@ -85,6 +86,7 @@ const SMOKE_CASES: SmokeCase[] = [ { file: 'ColorDepth.tsx', label: 'ColorDepth', effect: (ref) => }, { file: 'Depth.tsx', label: 'Depth', effect: (ref) => }, { file: 'DepthOfField.tsx', label: 'DepthOfField', effect: (ref) => }, + { file: 'DepthPicking.tsx', label: 'DepthPicking', effect: (ref) => }, { file: 'DotScreen.tsx', label: 'DotScreen', effect: (ref) => }, { file: 'FXAA.tsx', label: 'FXAA', effect: (ref) => }, { file: 'Glitch.tsx', label: 'Glitch', effect: (ref) => }, @@ -96,8 +98,7 @@ const SMOKE_CASES: SmokeCase[] = [ }, { file: 'Grid.tsx', label: 'Grid', effect: (ref) => }, { file: 'HueSaturation.tsx', label: 'HueSaturation', effect: (ref) => }, - // LensFlare manages its own internal ref and doesn't accept one as a prop. - { file: 'LensFlare.tsx', label: 'LensFlare', effect: () => }, + { file: 'LensFlare.tsx', label: 'LensFlare', effect: (ref) => }, { file: 'LUT.tsx', label: 'LUT', effect: (ref) => }, { file: 'N8AO.tsx', label: 'N8AO', effect: (ref) => }, { file: 'Noise.tsx', label: 'Noise', effect: (ref) => }, @@ -165,32 +166,12 @@ describe('effect smoke tests', () => { } }) - // Tracks dispose() calls per instance rather than per class — EffectComposerImpl - // constructs its own internal CopyPass (this.copyPass, for compositing) and - // disposes it as part of its own teardown, unrelated to any CopyPass an effect - // constructs. A class-wide spy would conflate the two into a false "double - // dispose"; this only flags it if the *same* instance is disposed twice. - function trackDisposePerInstance(Ctor: { prototype: { dispose: (...args: unknown[]) => unknown } }) { - const counts = new Map() - const original = Ctor.prototype.dispose - const spy = vi.spyOn(Ctor.prototype, 'dispose').mockImplementation(function (this: object, ...args: unknown[]) { - counts.set(this, (counts.get(this) ?? 0) + 1) - return original.apply(this, args) - }) - return { - restore: () => spy.mockRestore(), - maxCallsForAnySingleInstance: () => Math.max(0, ...counts.values()), - } - } - - // Autofocus's ref resolves to { dofRef, hitpoint, update } (its own - // imperative API), not an effect instance — the generic dispose check - // above silently no-ops for it. It actually owns three disposables - // (depthPickingPass, copyPass, and the DepthOfField effect it renders - // internally), verified explicitly here instead. - it('Autofocus disposes depthPickingPass, copyPass, and the nested DepthOfField effect exactly once each', async () => { - const depthPickingTracker = trackDisposePerInstance(DepthPickingPass) - const copyPassTracker = trackDisposePerInstance(CopyPass) + // Autofocus's ref resolves to { dofRef, hitpoint, update }, not an effect + // instance - the generic dispose check above no-ops for it. It owns two + // disposables (the nested DepthPicking component's pass, and the + // nested DepthOfField effect), verified here. + it('Autofocus disposes the nested DepthPicking and DepthOfField effect', async () => { + const depthPickingDisposeSpy = vi.spyOn(DepthPickingPassImpl.prototype, 'dispose') // AutofocusProps' `ref` type is broken (ComponentProps // drags in DepthOfField's own `ref: Ref`, which then // intersects with `Ref` — separate pre-existing issue, @@ -214,17 +195,40 @@ describe('effect smoke tests', () => { await React.act(async () => root.render(null)) await flush() - expect(depthPickingTracker.maxCallsForAnySingleInstance()).toBeLessThanOrEqual(1) - expect(copyPassTracker.maxCallsForAnySingleInstance()).toBeLessThanOrEqual(1) - expect(dofDisposeSpy).toHaveBeenCalledTimes(1) + expect(depthPickingDisposeSpy).toHaveBeenCalled() + expect(dofDisposeSpy).toHaveBeenCalled() + + depthPickingDisposeSpy.mockRestore() + }) + + it('DepthPicking disposes its pass on unmount', async () => { + const disposeSpy = vi.spyOn(DepthPickingPassImpl.prototype, 'dispose') + const ref = React.createRef() + + await React.act(async () => + root.render( + + + + ) + ) + + await flush() + expect(ref.current).toBeTruthy() + + await React.act(async () => root.render(null)) + await flush() + + expect(disposeSpy).toHaveBeenCalled() - depthPickingTracker.restore() - copyPassTracker.restore() + disposeSpy.mockRestore() }) - it('covers every file in src/effects (or documents why it is excluded)', () => { - const effectsDir = path.join(path.dirname(fileURLToPath(import.meta.url)), '..', 'effects') - const files = fs.readdirSync(effectsDir).filter((f) => f.endsWith('.tsx')) + it('covers every file in src/effects and src/passes (or documents why it is excluded)', () => { + const srcDir = path.join(path.dirname(fileURLToPath(import.meta.url)), '..') + const files = ['effects', 'passes'].flatMap((dir) => + fs.readdirSync(path.join(srcDir, dir)).filter((f) => f.endsWith('.tsx')) + ) const covered = new Set(SMOKE_CASES.map((c) => c.file)) const missing = files.filter((f) => !covered.has(f) && !(f in EXCLUDED)) diff --git a/src/util.tsx b/src/util.tsx index e45d9e6d..f446f049 100644 --- a/src/util.tsx +++ b/src/util.tsx @@ -1,7 +1,7 @@ -import { useThree, type ReactThreeFiber } from '@react-three/fiber' +import { useThree, type Instance, type ReactThreeFiber } from '@react-three/fiber' import type { Selection as PPSelection } from 'postprocessing' -import { use, useEffect, useMemo, useRef, type RefObject } from 'react' -import { Object3D, Vector2, type Vector2Tuple } from 'three' +import { use, useCallback, useEffect, useLayoutEffect, useMemo, useRef, type Ref, type RefObject } from 'react' +import { Group, Object3D, Vector2, Vector3, type Vector2Tuple, type Vector3Tuple } from 'three' import { selectionContext } from './Selection' // Stable reference for array-typed props defaulting to "nothing" - `= []` @@ -12,13 +12,49 @@ export const EMPTY_ARRAY: never[] = [] export const resolveRef = (ref: T | RefObject) => typeof ref === 'object' && ref != null && 'current' in ref ? ref.current : ref -/** - * Keeps a postprocessing effect's `selection` (and its render layer) in - * sync with either mode effects support: the manual - * `selection` prop (used only when there's no enclosing ), or - * the declarative Selection/Select API. The two are mutually exclusive - - * context wins when both are present. - */ +// Merges a local ref with a caller-supplied ref (object or callback form). +// Clears localRef inside the callback ref's own returned cleanup, since +// React 19 never re-invokes it with null once it returns one. +export function useMergeRefs( + localRef: RefObject, + outerRef: Ref | undefined +): (instance: T | null) => void | (() => void) { + return useCallback( + (instance: T | null) => { + localRef.current = instance + if (typeof outerRef !== 'function') { + if (outerRef) outerRef.current = instance + return + } + const cleanup = outerRef(instance) + if (typeof cleanup !== 'function') return + return () => { + localRef.current = null + cleanup() + } + }, + [localRef, outerRef] + ) +} + +// Reads a 's direct r3f children, filtered by type - transparent to +// non-host wrapper components, since r3f's instance tree already is. +export function readGroupChildren(group: Group, filter: (object: unknown) => object is T): T[] { + const groupInstance = (group as Group & { __r3f: Instance }).__r3f + return groupInstance ? groupInstance.children.map((child) => child.object).filter(filter) : [] +} + +// Diffs `next` against what's stored in `ref`, updating it only when the +// contents actually changed. Returns whether it changed. +export function updateIfChanged(ref: RefObject, next: T[]): boolean { + const previous = ref.current + if (next.length === previous.length && next.every((item, i) => item === previous[i])) return false + ref.current = next + return true +} + +// Keeps `selection` synced with whichever mode is active: the manual +// `selection` prop, or the declarative Selection/Select API (context wins). export function useSelectionSync( effect: { selection: PPSelection }, selection: Object3D | Object3D[] | RefObject | RefObject[], @@ -59,26 +95,91 @@ export function useSelectionSync( }, [api, effect.selection, invalidate]) } -/** - * r3f never disposes objects (their state may be owned outside - * React), so effects rendered that way must dispose themselves. Guards - * against double-dispose across StrictMode's dev-only mount/cleanup/mount - * cycle, where the cleanup closure re-runs against the same instance. - */ +// r3f never disposes objects, so they must dispose themselves. export const useDispose = void }>(instance: T): void => { - const disposedRef = useRef>(new WeakSet()) - useEffect(() => { - const disposed = disposedRef.current return () => { - if (instance && typeof instance === 'object' && !disposed.has(instance)) { - disposed.add(instance) - instance.dispose?.() - } + instance?.dispose?.() } }, [instance]) } +// Reads a plain or r3f-pierced ("a-b-c") key off an object, matching what +// applyProps below can write - a single reader that works for both shapes. +export function readPierced(instance: object, key: string): unknown { + let target: unknown = instance + for (const part of key.split('-')) { + if (target == null) return undefined + target = (target as Record)[part] + } + return target +} + +// Writes a plain or r3f-pierced ("a-b-c") key, mirroring readPierced. +// Deliberately a plain assignment, not r3f's applyProps: these instances +// are built with `new`, not r3f's reconciler, so applyProps' Color/Vector +// coercion never applies to them. Callers wrap values that need coercion. +export function applyPierced(instance: object, key: string, value: unknown): void { + const parts = key.split('-') + let target: unknown = instance + for (let idx = 0; idx < parts.length - 1; idx++) { + if (target == null) return + target = (target as Record)[parts[idx]] + } + if (target == null) return + ;(target as Record)[parts[parts.length - 1]] = value +} + +// Applies live-mutable properties onto an instance built via `new` (not +// r3f's reconciler, so r3f's own prop diffing/reset never runs on it). +// Falls back to the constructor-time default when a value is `undefined`. +// Only calls `set` when the resolved value actually changed since the last +// apply - some setters have side effects beyond storing the value (e.g. +// OutlineEffect's `multisampling` disposes its render target on every set). +export function useLiveDefaults( + instance: T | RefObject | null, + values: Record, + keys: Iterable, + get: (instance: T, key: string) => unknown = readPierced, + set: (instance: T, key: string, value: unknown) => void = applyPierced +): void { + const snapshotRef = useRef<{ instance: T; defaults: Map; applied: Map } | null>( + null + ) + const invalidate = useThree((state) => state.invalidate) + + useLayoutEffect(() => { + const resolved = resolveRef(instance) + if (!resolved) return + if (snapshotRef.current?.instance !== resolved) { + snapshotRef.current = { instance: resolved, defaults: new Map(), applied: new Map() } + } + const { defaults, applied } = snapshotRef.current + let changed = false + + for (const key of keys) { + if (!defaults.has(key)) { + // Seed `applied` too, not just `defaults`, so an unchanged key + // skips `set` even on this first pass (avoids re-triggering + // setters with side effects, e.g. multisampling's dispose). + const current = get(resolved, key) + defaults.set(key, current) + applied.set(key, current) + } + const next = values[key] !== undefined ? values[key] : defaults.get(key) + if (Object.is(applied.get(key), next)) continue + set(resolved, key, next) + applied.set(key, next) + changed = true + } + + // These instances are mutated directly (not via r3f's reconciler), so + // r3f never sees the change - without this, frameloop="demand" would + // never repaint after a live prop update. + if (changed) invalidate() + }) +} + export const useVector2 = (props: Record, key: string): Vector2 => { const value = props[key] as ReactThreeFiber.Vector2 | undefined @@ -87,6 +188,10 @@ export const useVector2 = (props: Record, key: string): Vector2 return new Vector2(value, value) } + if (value instanceof Vector2) { + return value + } + if (value) { return new Vector2(...(value as Vector2Tuple)) } @@ -94,3 +199,23 @@ export const useVector2 = (props: Record, key: string): Vector2 return new Vector2() }, [value]) } + +export const useVector3 = (props: Record, key: string): Vector3 => { + const value = props[key] as ReactThreeFiber.Vector3 | undefined + + return useMemo(() => { + if (typeof value === 'number') { + return new Vector3(value, value, value) + } + + if (value instanceof Vector3) { + return value + } + + if (value) { + return new Vector3(...(value as Vector3Tuple)) + } + + return new Vector3() + }, [value]) +} diff --git a/src/wrapEffect.tsx b/src/wrapEffect.tsx index 6fc1f449..2f9933d2 100644 --- a/src/wrapEffect.tsx +++ b/src/wrapEffect.tsx @@ -1,8 +1,9 @@ import { extend, useThree } from '@react-three/fiber' -import type { BlendFunction, Effect, Pass } from 'postprocessing' +import type { BlendFunction } from 'postprocessing' import { useMemo, type ExoticComponent, type JSX, type Ref } from 'react' +import type { EffectConstructor } from './createEffectComponent' -export type EffectConstructor = new (...args: any[]) => Effect | Pass +export type { EffectConstructor } // Handles three ConstructorParameters shapes: required first param // (P), optional first param (Partial

— some effects in postprocessing diff --git a/tsconfig.build.json b/tsconfig.build.json new file mode 100644 index 00000000..e625eeec --- /dev/null +++ b/tsconfig.build.json @@ -0,0 +1,4 @@ +{ + "extends": "./tsconfig.json", + "exclude": ["src/tests/**/*"] +} diff --git a/tsconfig.json b/tsconfig.json index 223828ef..6a8324fc 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -17,6 +17,5 @@ "@react-three/postprocessing": ["./src"] } }, - "include": ["src/**/*"], - "exclude": ["src/**/*.test.*"] + "include": ["src/**/*"] } \ No newline at end of file diff --git a/vite.config.ts b/vite.config.ts index 1a97d0d9..b283f2ea 100644 --- a/vite.config.ts +++ b/vite.config.ts @@ -10,6 +10,7 @@ export default defineConfig({ }, build: { sourcemap: true, + minify: false, lib: { formats: ['es'], entry: 'src/index.ts', diff --git a/yarn.lock b/yarn.lock deleted file mode 100644 index b00b2316..00000000 --- a/yarn.lock +++ /dev/null @@ -1,2935 +0,0 @@ -# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. -# yarn lockfile v1 - - -"@babel/code-frame@^7.29.7": - version "7.29.7" - resolved "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.29.7.tgz" - integrity sha512-Aup7aUOfpbAUg2ROOJN6Iw5f9DMBlzu0mIkm/malLQFN/YQgO48wCj0Kxa3sEHJvPVFg7siR+qRInwXd2qhQKw== - dependencies: - "@babel/helper-validator-identifier" "^7.29.7" - js-tokens "^4.0.0" - picocolors "^1.1.1" - -"@babel/compat-data@^7.29.7": - version "7.29.7" - resolved "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.29.7.tgz" - integrity sha512-locTkQyKvwIEgBzVrn8693ebc97F2U8ZHjbXwDXJ5Fn2TCpNwTlKcaKLkdHop5c/icOFE7qt7Q9JC5hnKNa6Gg== - -"@babel/core@^7.24.4": - version "7.29.7" - resolved "https://registry.npmjs.org/@babel/core/-/core-7.29.7.tgz" - integrity sha512-RgHBCvtjbOK2gXSNBNIkNoEc9qoVEtau3hj8gEqKQuL3HZAibKarWFEI3Lfm6EYKkLalOh8eSrj9b+ch9H/VBA== - dependencies: - "@babel/code-frame" "^7.29.7" - "@babel/generator" "^7.29.7" - "@babel/helper-compilation-targets" "^7.29.7" - "@babel/helper-module-transforms" "^7.29.7" - "@babel/helpers" "^7.29.7" - "@babel/parser" "^7.29.7" - "@babel/template" "^7.29.7" - "@babel/traverse" "^7.29.7" - "@babel/types" "^7.29.7" - "@jridgewell/remapping" "^2.3.5" - convert-source-map "^2.0.0" - debug "^4.1.0" - gensync "^1.0.0-beta.2" - json5 "^2.2.3" - semver "^6.3.1" - -"@babel/generator@^7.29.7": - version "7.29.7" - resolved "https://registry.npmjs.org/@babel/generator/-/generator-7.29.7.tgz" - integrity sha512-DkXD5OJQaAQIdZ1bt3UZdEnHAn9Imd3IVBdX03UFe+ony9Ojw5pzr9YVKGDY1jt+Gcn/FnGkNf8r+Vj5NOJWtQ== - dependencies: - "@babel/parser" "^7.29.7" - "@babel/types" "^7.29.7" - "@jridgewell/gen-mapping" "^0.3.12" - "@jridgewell/trace-mapping" "^0.3.28" - jsesc "^3.0.2" - -"@babel/helper-compilation-targets@^7.29.7": - version "7.29.7" - resolved "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.29.7.tgz" - integrity sha512-wem6WaBj4NaVYVdNhLPPVacES6ZJ+KBBfSkTMD3YZxbP3rm3Di85tJU5ljaUNhaOynt+Aj0xruhYuzQBt8n71g== - dependencies: - "@babel/compat-data" "^7.29.7" - "@babel/helper-validator-option" "^7.29.7" - browserslist "^4.24.0" - lru-cache "^5.1.1" - semver "^6.3.1" - -"@babel/helper-globals@^7.29.7": - version "7.29.7" - resolved "https://registry.npmjs.org/@babel/helper-globals/-/helper-globals-7.29.7.tgz" - integrity sha512-3nQVUAtvkKH9zahfWgw96Jc/uFOmjACE1kQz82E2lqWmHBgjzbNlsC22nuQTfahmWeQtTq5nQ/4Nnd2A1wj4zA== - -"@babel/helper-module-imports@^7.29.7": - version "7.29.7" - resolved "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.29.7.tgz" - integrity sha512-ejHwrQQYcm9xnTivShn2IDOlIzInN34AXskvq9QicvCtEzq1Vzclu/tKF8Jq1Cg8JG2GL6/EmjgsCT7lXepE3g== - dependencies: - "@babel/traverse" "^7.29.7" - "@babel/types" "^7.29.7" - -"@babel/helper-module-transforms@^7.29.7": - version "7.29.7" - resolved "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.29.7.tgz" - integrity sha512-UPUVSyXbOh627KiCIGQSgwWzGeBKLkaJ9PJEdrngIwMSzxLR4jS4+f1f1jb7VzBbg8nFLaYotvVPFCTqdrmTAg== - dependencies: - "@babel/helper-module-imports" "^7.29.7" - "@babel/helper-validator-identifier" "^7.29.7" - "@babel/traverse" "^7.29.7" - -"@babel/helper-string-parser@^7.29.7": - version "7.29.7" - resolved "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.29.7.tgz" - integrity sha512-Pb5ijPrZ89GDH8223L4UP8i6QApWxs04RbPQJTeWDV0/keR2E36MeKnyr6LYmUUvqRRI+Iv87SuF1W6ErINzYw== - -"@babel/helper-validator-identifier@^7.29.7": - version "7.29.7" - resolved "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.29.7.tgz" - integrity sha512-qehxGkRj55h/ff8EMaJ+cYhyaKlHIxqYDn682wQD7RNp9UujOQsHog2uS0r2vzr4pW+sXf90NeeayjcNaX3fFg== - -"@babel/helper-validator-option@^7.29.7": - version "7.29.7" - resolved "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.29.7.tgz" - integrity sha512-N9ZErrD+yW5geCDtBqnOoxmR8+tNKiGuxKlDpuJxfsqpa2dFcexaziGAE/qoHLiDDreVNMupxGmSoNlyvsA3gw== - -"@babel/helpers@^7.29.7": - version "7.29.7" - resolved "https://registry.npmjs.org/@babel/helpers/-/helpers-7.29.7.tgz" - integrity sha512-1k2lAGRMfHTcwuNYcCNUmaUffmQv8KWMfh2iJUUeRlwlwH4FdNG7mfPI10NPfLHJFThE4Tyr4mv7kTNZOiPuBg== - dependencies: - "@babel/template" "^7.29.7" - "@babel/types" "^7.29.7" - -"@babel/parser@^7.24.4", "@babel/parser@^7.29.7": - version "7.29.7" - resolved "https://registry.npmjs.org/@babel/parser/-/parser-7.29.7.tgz" - integrity sha512-hnORnjP/1P/zFEndoeX+n+t1RwWRJiJpM/jO7FW32Kn9r5+sJB2JWOdYo4L6k78j15eCwY3Gm/7364B1EMwtNg== - dependencies: - "@babel/types" "^7.29.7" - -"@babel/runtime@^7.17.8": - version "7.29.7" - resolved "https://registry.npmjs.org/@babel/runtime/-/runtime-7.29.7.tgz" - integrity sha512-Nq8OhGWiZIZGV6hLHoyAKLLcJihP/xFeBMGJoUrxTX2psI8dCifzLhZISFb+VWS3wFMRDmCGw5R+dOySCqPLhw== - -"@babel/template@^7.29.7": - version "7.29.7" - resolved "https://registry.npmjs.org/@babel/template/-/template-7.29.7.tgz" - integrity sha512-puq+Gf35oI24FeN11LkoUQFqv9uwNeWpxXZi/Ji3rRIoKAzKnxRaZ+Gkj0vKS9ZCiTESfng1N9LyOyXvo+m+Gg== - dependencies: - "@babel/code-frame" "^7.29.7" - "@babel/parser" "^7.29.7" - "@babel/types" "^7.29.7" - -"@babel/traverse@^7.29.7": - version "7.29.7" - resolved "https://registry.npmjs.org/@babel/traverse/-/traverse-7.29.7.tgz" - integrity sha512-EhlfNQtZ+NK22w5BM61ciuiq1m58ed33Wr1Xan//ZRTy6hgjnwyCffRYwzsGXdASJSUJ1guZILsErh1eQcl+zw== - dependencies: - "@babel/code-frame" "^7.29.7" - "@babel/generator" "^7.29.7" - "@babel/helper-globals" "^7.29.7" - "@babel/parser" "^7.29.7" - "@babel/template" "^7.29.7" - "@babel/types" "^7.29.7" - debug "^4.3.1" - -"@babel/types@^7.29.7": - version "7.29.7" - resolved "https://registry.npmjs.org/@babel/types/-/types-7.29.7.tgz" - integrity sha512-4zBIxpPzowiZpusoFkyGVwakdRJUyuH5PxQ/PrqghfdFWWasvnCdPfQXHrenDai+gyLARulZjZowCOj6fjT4pA== - dependencies: - "@babel/helper-string-parser" "^7.29.7" - "@babel/helper-validator-identifier" "^7.29.7" - -"@dimforge/rapier3d-compat@~0.12.0": - version "0.12.0" - resolved "https://registry.npmjs.org/@dimforge/rapier3d-compat/-/rapier3d-compat-0.12.0.tgz" - integrity sha512-uekIGetywIgopfD97oDL5PfeezkFpNhwlzlaEYNOA0N6ghdsOvh/HYjSMek5Q2O1PYvRSDFcqFVJl4r4ZBwOow== - -"@emnapi/core@1.11.1": - version "1.11.1" - resolved "https://registry.yarnpkg.com/@emnapi/core/-/core-1.11.1.tgz#b9e1064f3a6b1631e241e638eb48d736bfd372a6" - integrity sha512-RSvbQmHzdKzNsLYa/wHrbc3KN4sYLKAdPZxqiM2HATqv/SBk2/ENSHpvXGaLOMcsAyz0poEGqkmmKYG3OWiJEQ== - dependencies: - "@emnapi/wasi-threads" "1.2.2" - tslib "^2.4.0" - -"@emnapi/runtime@1.11.1": - version "1.11.1" - resolved "https://registry.yarnpkg.com/@emnapi/runtime/-/runtime-1.11.1.tgz#58f1f3d5d81a9b12f793ab688c96371901027c24" - integrity sha512-vgj7R3y3Wgx24IQaGPA/R6YFXLHVMOZ0uVEyIQPaWs+rd1AzfEMXlAC22FYwO1XkKR6NPsq7mUandH8oIRdZFw== - dependencies: - tslib "^2.4.0" - -"@emnapi/wasi-threads@1.2.2": - version "1.2.2" - resolved "https://registry.yarnpkg.com/@emnapi/wasi-threads/-/wasi-threads-1.2.2.tgz#4c93becf5bfa3b13d1bbdcc06aee38321ad8139a" - integrity sha512-c95qOXkHdydNKhscBTebqEC1CVAZpyqOfVfBzQ1qgzyl3gfeldUjIggDbIZgDKsHLgnsM+igH7TJ/eAasaVuMA== - dependencies: - tslib "^2.4.0" - -"@eslint-community/eslint-utils@^4.8.0", "@eslint-community/eslint-utils@^4.9.1": - version "4.10.1" - resolved "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.10.1.tgz" - integrity sha512-cuadcxVFE8sDK6iWJbs8Sn0av2Nrh2QSGQhVlBW9AaAHqHwjWsZHT8LJ4hFGPh7ASBV2deFdM7H/DPjulmh8rg== - dependencies: - eslint-visitor-keys "^3.4.3" - -"@eslint-community/regexpp@^4.12.1", "@eslint-community/regexpp@^4.12.2": - version "4.12.2" - resolved "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.12.2.tgz" - integrity sha512-EriSTlt5OC9/7SXkRSCAhfSxxoSUgBm33OH+IkwbdpgoqsSsUg7y3uh+IICI/Qg4BBWr3U2i39RpmycbxMq4ew== - -"@eslint/compat@^2.1.0": - version "2.1.0" - resolved "https://registry.npmjs.org/@eslint/compat/-/compat-2.1.0.tgz" - integrity sha512-LgaSCymEpw7tF53xvDw9SNsraPb1IBHxpdABIOM0hW8UAlP8znrjYtuxfR58FSJ3L9BhwD+FaPRFQpZq84Nh6g== - dependencies: - "@eslint/core" "^1.2.1" - -"@eslint/config-array@^0.21.2": - version "0.21.2" - resolved "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.21.2.tgz" - integrity sha512-nJl2KGTlrf9GjLimgIru+V/mzgSK0ABCDQRvxw5BjURL7WfH5uoWmizbH7QB6MmnMBd8cIC9uceWnezL1VZWWw== - dependencies: - "@eslint/object-schema" "^2.1.7" - debug "^4.3.1" - minimatch "^3.1.5" - -"@eslint/config-helpers@^0.4.2": - version "0.4.2" - resolved "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.4.2.tgz" - integrity sha512-gBrxN88gOIf3R7ja5K9slwNayVcZgK6SOUORm2uBzTeIEfeVaIhOpCtTox3P6R7o2jLFwLFTLnC7kU/RGcYEgw== - dependencies: - "@eslint/core" "^0.17.0" - -"@eslint/core@^0.17.0": - version "0.17.0" - resolved "https://registry.npmjs.org/@eslint/core/-/core-0.17.0.tgz" - integrity sha512-yL/sLrpmtDaFEiUj1osRP4TI2MDz1AddJL+jZ7KSqvBuliN4xqYY54IfdN8qD8Toa6g1iloph1fxQNkjOxrrpQ== - dependencies: - "@types/json-schema" "^7.0.15" - -"@eslint/core@^1.2.1": - version "1.2.1" - resolved "https://registry.npmjs.org/@eslint/core/-/core-1.2.1.tgz" - integrity sha512-MwcE1P+AZ4C6DWlpin/OmOA54mmIZ/+xZuJiQd4SyB29oAJjN30UW9wkKNptW2ctp4cEsvhlLY/CsQ1uoHDloQ== - dependencies: - "@types/json-schema" "^7.0.15" - -"@eslint/eslintrc@^3.3.6": - version "3.3.6" - resolved "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-3.3.6.tgz" - integrity sha512-l2Ul9PrHsPCKcEY/ac7VgFj9D80C7S68sOKc618SyHDPK36s1XcFebXY0iTzUVn4Yq+YbwvSnDmCz9yxjX+QrA== - dependencies: - ajv "^6.14.0" - debug "^4.3.2" - espree "^10.0.1" - globals "^14.0.0" - ignore "^5.2.0" - import-fresh "^3.2.1" - js-yaml "^4.3.0" - minimatch "^3.1.5" - strip-json-comments "^3.1.1" - -"@eslint/js@9.39.5", "@eslint/js@^9.39.5": - version "9.39.5" - resolved "https://registry.npmjs.org/@eslint/js/-/js-9.39.5.tgz" - integrity sha512-QywQuszQh77pIXCsq998c8hbhSTI/azTty1Z6N53dmAudKHhy573j3yvRLsX2BSp8YpLtoCEG8E9DJe+8zUh4A== - -"@eslint/object-schema@^2.1.7": - version "2.1.7" - resolved "https://registry.npmjs.org/@eslint/object-schema/-/object-schema-2.1.7.tgz" - integrity sha512-VtAOaymWVfZcmZbp6E2mympDIHvyjXs/12LqWYjVw6qjrfF+VK+fyG33kChz3nnK+SU5/NeHOqrTEHS8sXO3OA== - -"@eslint/plugin-kit@^0.4.1": - version "0.4.1" - resolved "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.4.1.tgz" - integrity sha512-43/qtrDUokr7LJqoF2c3+RInu/t4zfrpYdoSDfYyhg52rwLV6TnOvdG4fXm7IkSB3wErkcmJS9iEhjVtOSEjjA== - dependencies: - "@eslint/core" "^0.17.0" - levn "^0.4.1" - -"@humanfs/core@^0.19.2": - version "0.19.2" - resolved "https://registry.npmjs.org/@humanfs/core/-/core-0.19.2.tgz" - integrity sha512-UhXNm+CFMWcbChXywFwkmhqjs3PRCmcSa/hfBgLIb7oQ5HNb1wS0icWsGtSAUNgefHeI+eBrA8I1fxmbHsGdvA== - dependencies: - "@humanfs/types" "^0.15.0" - -"@humanfs/node@^0.16.6": - version "0.16.8" - resolved "https://registry.npmjs.org/@humanfs/node/-/node-0.16.8.tgz" - integrity sha512-gE1eQNZ3R++kTzFUpdGlpmy8kDZD/MLyHqDwqjkVQI0JMdI1D51sy1H958PNXYkM2rAac7e5/CnIKZrHtPh3BQ== - dependencies: - "@humanfs/core" "^0.19.2" - "@humanfs/types" "^0.15.0" - "@humanwhocodes/retry" "^0.4.0" - -"@humanfs/types@^0.15.0": - version "0.15.0" - resolved "https://registry.npmjs.org/@humanfs/types/-/types-0.15.0.tgz" - integrity sha512-ZZ1w0aoQkwuUuC7Yf+7sdeaNfqQiiLcSRbfI08oAxqLtpXQr9AIVX7Ay7HLDuiLYAaFPu8oBYNq/QIi9URHJ3Q== - -"@humanwhocodes/module-importer@^1.0.1": - version "1.0.1" - resolved "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz" - integrity sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA== - -"@humanwhocodes/retry@^0.4.0", "@humanwhocodes/retry@^0.4.2": - version "0.4.3" - resolved "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.4.3.tgz" - integrity sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ== - -"@jridgewell/gen-mapping@^0.3.12", "@jridgewell/gen-mapping@^0.3.5": - version "0.3.13" - resolved "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz" - integrity sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA== - dependencies: - "@jridgewell/sourcemap-codec" "^1.5.0" - "@jridgewell/trace-mapping" "^0.3.24" - -"@jridgewell/remapping@^2.3.5": - version "2.3.5" - resolved "https://registry.npmjs.org/@jridgewell/remapping/-/remapping-2.3.5.tgz" - integrity sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ== - dependencies: - "@jridgewell/gen-mapping" "^0.3.5" - "@jridgewell/trace-mapping" "^0.3.24" - -"@jridgewell/resolve-uri@^3.1.0": - version "3.1.2" - resolved "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz" - integrity sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw== - -"@jridgewell/sourcemap-codec@^1.4.14", "@jridgewell/sourcemap-codec@^1.5.0", "@jridgewell/sourcemap-codec@^1.5.5": - version "1.5.5" - resolved "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz" - integrity sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og== - -"@jridgewell/trace-mapping@^0.3.24", "@jridgewell/trace-mapping@^0.3.28": - version "0.3.31" - resolved "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz" - integrity sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw== - dependencies: - "@jridgewell/resolve-uri" "^3.1.0" - "@jridgewell/sourcemap-codec" "^1.4.14" - -"@napi-rs/wasm-runtime@^1.1.6": - version "1.2.2" - resolved "https://registry.yarnpkg.com/@napi-rs/wasm-runtime/-/wasm-runtime-1.2.2.tgz#c70706532e5827c0932ca6bf43ee2c512f29c639" - integrity sha512-JfB4kuJQjaoHuCTseIINHtHWeJnvgEcxjwA5t/Y00ZgaOO1Crz3fjT/p8kT28zA/Caz7oiUMn3d6H2yOVCVwuw== - dependencies: - "@tybys/wasm-util" "^0.10.3" - -"@oxc-project/types@=0.139.0": - version "0.139.0" - resolved "https://registry.npmjs.org/@oxc-project/types/-/types-0.139.0.tgz" - integrity sha512-r9gHphtCs+1M7J0pw6Sn/hh/Wpa/iQrOOkrNAlVLF/gHq+/CJmHIWKKUUhdWjcD6CIa8idarspCsASiXCXvFUw== - -"@pkgr/core@^0.3.6": - version "0.3.6" - resolved "https://registry.npmjs.org/@pkgr/core/-/core-0.3.6.tgz" - integrity sha512-SEeaJLb3qBNF/OaXnaR1NmmBbFYk1zC0ZH/52fATcRPLFg/p791YrcyFFy44Bo9sLaGuSuLp5Q6axbb/O+v/RA== - -"@react-three/fiber@^9.7.0": - version "9.7.0" - resolved "https://registry.yarnpkg.com/@react-three/fiber/-/fiber-9.7.0.tgz#3cb620eafe7ed39540d3ebef4280c7827966c206" - integrity sha512-EWm9FwcaOZQu/ExFW5rggoCMM1NJet5YbxVxKaOE+KSncrjU0Wx7017qSyGFvupviK89nMYGCWU3BIK4dI1clw== - dependencies: - "@babel/runtime" "^7.17.8" - "@types/webxr" "*" - base64-js "^1.5.1" - buffer "^6.0.3" - its-fine "^2.0.0" - react-use-measure "^2.1.7" - scheduler "^0.27.0" - suspend-react "^0.1.3" - use-sync-external-store "^1.4.0" - zustand "^5.0.3" - -"@rolldown/binding-android-arm64@1.1.5": - version "1.1.5" - resolved "https://registry.yarnpkg.com/@rolldown/binding-android-arm64/-/binding-android-arm64-1.1.5.tgz#f58cb9a0a8128ed0582282720528547fc5c035f3" - integrity sha512-lZg8fqIv2v7FF237bwMgzGZEJvGL79/s5knJ/i6FmsGF4XXlzccZ4jb+TrFIxtSSxFtIpdsgrPZeMk1I9AFcyQ== - -"@rolldown/binding-darwin-arm64@1.1.5": - version "1.1.5" - resolved "https://registry.yarnpkg.com/@rolldown/binding-darwin-arm64/-/binding-darwin-arm64-1.1.5.tgz#441144c05a4a831aa75269abc3a4a324374ea707" - integrity sha512-51Bnx9pNiMRKSUNtBfySkNJ9vMU9Hh3I1ozDd6gyPPYzaXCfnptUcEZxXGYFn+ul2dtcMUiqGR1Yai2K10uoTw== - -"@rolldown/binding-darwin-x64@1.1.5": - version "1.1.5" - resolved "https://registry.yarnpkg.com/@rolldown/binding-darwin-x64/-/binding-darwin-x64-1.1.5.tgz#c82e30652cef52c4af925d5c66c8955a40319816" - integrity sha512-Tm+gbfC0aHu1tBA/JvKQh32S0K6YgCHkiAF4/W6xX0K0RmNuc94VeK419dJoE65R5aRxmo+noZQSWrAMF6yb6g== - -"@rolldown/binding-freebsd-x64@1.1.5": - version "1.1.5" - resolved "https://registry.yarnpkg.com/@rolldown/binding-freebsd-x64/-/binding-freebsd-x64-1.1.5.tgz#c32e9ce7fa1c0fb2b80913a2a3a05c3e907d06b0" - integrity sha512-JMzDKCCXq93YccG5gz3hvOs1oXRKAf0XYpfOS88e+wZrC8Iugj6j68867vrYZkvpDDpKn/KoKORThmchMpF6TA== - -"@rolldown/binding-linux-arm-gnueabihf@1.1.5": - version "1.1.5" - resolved "https://registry.yarnpkg.com/@rolldown/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-1.1.5.tgz#ce90b5e22316adeb502ea010582f498cd0604f27" - integrity sha512-uML21j2K5TfPGutKxub+M+nLjZIrWjXQ5Grx4lCe/nimTj9B4L63zHpjXLl4y0L3mcm2htEQIb06oCG/szerNw== - -"@rolldown/binding-linux-arm64-gnu@1.1.5": - version "1.1.5" - resolved "https://registry.yarnpkg.com/@rolldown/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-1.1.5.tgz#91947110c4ddaa4eefb004e52688070977085201" - integrity sha512-navSiuTMogvnQoZoM/v+l3ZWo50/NTwSHSzheABx/RCnmUPaKwq9qSo4Br2OYRs21+Fz8uFqITZM3H4opOB0/Q== - -"@rolldown/binding-linux-arm64-musl@1.1.5": - version "1.1.5" - resolved "https://registry.yarnpkg.com/@rolldown/binding-linux-arm64-musl/-/binding-linux-arm64-musl-1.1.5.tgz#eb32b2d4108c1c702b91e8cde8a043eae5caa94d" - integrity sha512-lAryqH7IteztmCXQXk0etKj4wBQ7Gx5S6LjKhsgp9zb8I5bsuvU/2llH1hDQcjsFeqIsovMVN339/8pUDDBXxA== - -"@rolldown/binding-linux-ppc64-gnu@1.1.5": - version "1.1.5" - resolved "https://registry.yarnpkg.com/@rolldown/binding-linux-ppc64-gnu/-/binding-linux-ppc64-gnu-1.1.5.tgz#ccb943c11e5a72655cbb02fc1163541ceb640782" - integrity sha512-fsK/sNBnxzBlL4O1JNrZakVQxPspqpED5dLtNsZS9oOKmtSpdNIzxH2kkol5HYTWJN47sE20ztMJPxfZ89qGOg== - -"@rolldown/binding-linux-s390x-gnu@1.1.5": - version "1.1.5" - resolved "https://registry.yarnpkg.com/@rolldown/binding-linux-s390x-gnu/-/binding-linux-s390x-gnu-1.1.5.tgz#a33731ee567e90b75fac6e5e55307e8a2b3038f0" - integrity sha512-gLYb4BIadlfTOYT5gO503n8zQjXflgzpD0FcyKh0Mzx3rqCZKnHoJWV9xe1KXUJ5lx2JfcSHr/mhzS0PC/McAA== - -"@rolldown/binding-linux-x64-gnu@1.1.5": - version "1.1.5" - resolved "https://registry.yarnpkg.com/@rolldown/binding-linux-x64-gnu/-/binding-linux-x64-gnu-1.1.5.tgz#a74c01aaacedfc11c39b6feba33a5fa0c654949f" - integrity sha512-FjcpEKUyJygHgs1o50VYNvkt5+7Le/VEdYt0AkRpkL33MnyQfwr8l5mXwMmfmTbyMPr5vJLC+8/Gd9gXnwU1QQ== - -"@rolldown/binding-linux-x64-musl@1.1.5": - version "1.1.5" - resolved "https://registry.yarnpkg.com/@rolldown/binding-linux-x64-musl/-/binding-linux-x64-musl-1.1.5.tgz#28cd178494fa1e65dba412229b5ce55c4dd5cbd1" - integrity sha512-Me+PfPI2TMeOQk0gYWfLQZtTktrmzbr8cDboqX83XKc7UrgAi55gF+2dUkWdxd19n55Essp2yeca+O9N5rBxHg== - -"@rolldown/binding-openharmony-arm64@1.1.5": - version "1.1.5" - resolved "https://registry.yarnpkg.com/@rolldown/binding-openharmony-arm64/-/binding-openharmony-arm64-1.1.5.tgz#f7c75fa913fc20884d26a7d488d4f5c597cd71c0" - integrity sha512-yc5WrLzXks6zCQfn9Oxr8pORKyl/pF+QjHmW/Qx3qu0oyrrNC+y2JLTU1E2rcWYAmzlnqngWXHQjy51VzW70Vw== - -"@rolldown/binding-wasm32-wasi@1.1.5": - version "1.1.5" - resolved "https://registry.yarnpkg.com/@rolldown/binding-wasm32-wasi/-/binding-wasm32-wasi-1.1.5.tgz#c379581947787081df363ea106140fcd5fec252d" - integrity sha512-VbQGPX2b4r48TAMIM2cjgluIM1HYutm4pcTEJsle7iEP7sB1dFqtPLBVbdLAZCxy1txCcPxf4QFf4v8uvltPqA== - dependencies: - "@emnapi/core" "1.11.1" - "@emnapi/runtime" "1.11.1" - "@napi-rs/wasm-runtime" "^1.1.6" - -"@rolldown/binding-win32-arm64-msvc@1.1.5": - version "1.1.5" - resolved "https://registry.yarnpkg.com/@rolldown/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-1.1.5.tgz#f23c88694f7a729a12f395024aee38df80a16ba3" - integrity sha512-gHv82k63z4qpV5+Q1y/12KrK0ltWBukVDI8nZcbT7Tt/ZlOIVwppazneq0F93oDxTo3IgAMEDIoQh3E2n6mVsw== - -"@rolldown/binding-win32-x64-msvc@1.1.5": - version "1.1.5" - resolved "https://registry.npmjs.org/@rolldown/binding-win32-x64-msvc/-/binding-win32-x64-msvc-1.1.5.tgz" - integrity sha512-tTZuDBPw85tEN5PQi1pnEBzDy0Z49HtScLAbD5t6hyeU92A95pRWaSMw1GZZi/RwgSgUIl0xrSlXIT/9QzvYSA== - -"@rolldown/pluginutils@^1.0.0": - version "1.0.1" - resolved "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.1.tgz" - integrity sha512-2j9bGt5Jh8hj+vPtgzPtl72j0yRxHAyumoo6TNfAjsLB04UtpSvPbPcDcBMxz7n+9CYB0c1GxQFxYRg2jimqGw== - -"@rtsao/scc@^1.1.0": - version "1.1.0" - resolved "https://registry.npmjs.org/@rtsao/scc/-/scc-1.1.0.tgz" - integrity sha512-zt6OdqaDoOnJ1ZYsCYGt9YmWzDXl4vQdKTyJev62gFhRGKdx7mcT54V9KIjg+d2wi9EXsPvAPKe7i7WjfVWB8g== - -"@standard-schema/spec@^1.1.0": - version "1.1.0" - resolved "https://registry.npmjs.org/@standard-schema/spec/-/spec-1.1.0.tgz" - integrity sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w== - -"@tweenjs/tween.js@~23.1.3": - version "23.1.3" - resolved "https://registry.npmjs.org/@tweenjs/tween.js/-/tween.js-23.1.3.tgz" - integrity sha512-vJmvvwFxYuGnF2axRtPYocag6Clbb5YS7kLL+SO/TeVFzHqDIWrNKYtcsPMibjDx9O+bu+psAy9NKfWklassUA== - -"@tybys/wasm-util@^0.10.3": - version "0.10.3" - resolved "https://registry.yarnpkg.com/@tybys/wasm-util/-/wasm-util-0.10.3.tgz#015cba9e9dd47ce14d03d2a8c5d547bfb169665d" - integrity sha512-F3fo1MYrRJYL3zER0OUOmkutjr1Vp23m7OsSgp7nq4SP6OqX6C/56XFIPAl5bt3zaBRjmW7SGz3u/6LwFpYcOg== - dependencies: - tslib "^2.4.0" - -"@types/chai@^5.2.2": - version "5.2.3" - resolved "https://registry.npmjs.org/@types/chai/-/chai-5.2.3.tgz" - integrity sha512-Mw558oeA9fFbv65/y4mHtXDs9bPnFMZAL/jxdPFUpOHHIXX91mcgEHbS5Lahr+pwZFR8A7GQleRWeI6cGFC2UA== - dependencies: - "@types/deep-eql" "*" - assertion-error "^2.0.1" - -"@types/deep-eql@*": - version "4.0.2" - resolved "https://registry.npmjs.org/@types/deep-eql/-/deep-eql-4.0.2.tgz" - integrity sha512-c9h9dVVMigMPc4bwTvC5dxqtqJZwQPePsWjPlpSOnojbor6pGqdk541lfA7AqFQr5pB1BRdq0juY9db81BwyFw== - -"@types/estree@^1.0.0", "@types/estree@^1.0.6": - version "1.0.9" - resolved "https://registry.npmjs.org/@types/estree/-/estree-1.0.9.tgz" - integrity sha512-GhdPgy1el4/ImP05X05Uw4cw2/M93BCUmnEvWZNStlCzEKME4Fkk+YpoA5OiHNQmoS7Cafb8Xa3Pya8m1Qrzeg== - -"@types/json-schema@^7.0.15": - version "7.0.15" - resolved "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz" - integrity sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA== - -"@types/json5@^0.0.29": - version "0.0.29" - resolved "https://registry.npmjs.org/@types/json5/-/json5-0.0.29.tgz" - integrity sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ== - -"@types/node@^26.1.1": - version "26.1.1" - resolved "https://registry.npmjs.org/@types/node/-/node-26.1.1.tgz" - integrity sha512-nxAkRSVkN1Y0JC1W8ky/fTfkGsMmcrRsbx+3XoZE+rMOX71kLYTV7fLXpqud1GpbpP5TuffXFqfX7fH2GgZREw== - dependencies: - undici-types "~8.3.0" - -"@types/react-reconciler@^0.28.9": - version "0.28.9" - resolved "https://registry.npmjs.org/@types/react-reconciler/-/react-reconciler-0.28.9.tgz" - integrity sha512-HHM3nxyUZ3zAylX8ZEyrDNd2XZOnQ0D5XfunJF5FLQnZbHHYq4UWvW1QfelQNXv1ICNkwYhfxjwfnqivYB6bFg== - -"@types/react@^19.2.17": - version "19.2.17" - resolved "https://registry.npmjs.org/@types/react/-/react-19.2.17.tgz" - integrity sha512-MXfmqaVPEVgkBT/aY0aGCkRWWtByiYQXo3xdQ8r5RzuFrPiRn8Gar2tQdXSUQ2GKV3bkXckek89V8wQBY2Q/Aw== - dependencies: - csstype "^3.2.2" - -"@types/stats.js@*": - version "0.17.4" - resolved "https://registry.npmjs.org/@types/stats.js/-/stats.js-0.17.4.tgz" - integrity sha512-jIBvWWShCvlBqBNIZt0KAshWpvSjhkwkEu4ZUcASoAvhmrgAUI2t1dXrjSL4xXVLB4FznPrIsX3nKXFl/Dt4vA== - -"@types/three@^0.182.0": - version "0.182.0" - resolved "https://registry.npmjs.org/@types/three/-/three-0.182.0.tgz" - integrity sha512-WByN9V3Sbwbe2OkWuSGyoqQO8Du6yhYaXtXLoA5FkKTUJorZ+yOHBZ35zUUPQXlAKABZmbYp5oAqpA4RBjtJ/Q== - dependencies: - "@dimforge/rapier3d-compat" "~0.12.0" - "@tweenjs/tween.js" "~23.1.3" - "@types/stats.js" "*" - "@types/webxr" ">=0.5.17" - "@webgpu/types" "*" - fflate "~0.8.2" - meshoptimizer "~0.22.0" - -"@types/webxr@*", "@types/webxr@>=0.5.17": - version "0.5.24" - resolved "https://registry.npmjs.org/@types/webxr/-/webxr-0.5.24.tgz" - integrity sha512-h8fgEd/DpoS9CBrjEQXR+dIDraopAEfu4wYVNY2tEPwk60stPWhvZMf4Foo5FakuQ7HFZoa8WceaWFervK2Ovg== - -"@typescript-eslint/eslint-plugin@^8.64.0": - version "8.65.0" - resolved "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.65.0.tgz" - integrity sha512-IEgob78X12rHpUmtcwFsXhZdVGJtwTVP8FiCLZkR6GlYVrl2PcuB+KhCE5BlVC/eQpQnu8WXRtkHZuPar+gCRA== - dependencies: - "@eslint-community/regexpp" "^4.12.2" - "@typescript-eslint/scope-manager" "8.65.0" - "@typescript-eslint/type-utils" "8.65.0" - "@typescript-eslint/utils" "8.65.0" - "@typescript-eslint/visitor-keys" "8.65.0" - ignore "^7.0.5" - natural-compare "^1.4.0" - ts-api-utils "^2.5.0" - -"@typescript-eslint/parser@^8.64.0": - version "8.65.0" - resolved "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.65.0.tgz" - integrity sha512-CZ4nMxWwgu1HEEFNkeaCptra9QCtkmKdgf3sWh1rl1trIhmxLilgTV4cwcbQ4wemnT4sWQN8CaKOmdYx+g2gMA== - dependencies: - "@typescript-eslint/scope-manager" "8.65.0" - "@typescript-eslint/types" "8.65.0" - "@typescript-eslint/typescript-estree" "8.65.0" - "@typescript-eslint/visitor-keys" "8.65.0" - debug "^4.4.3" - -"@typescript-eslint/project-service@8.65.0": - version "8.65.0" - resolved "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.65.0.tgz" - integrity sha512-SxnPhbTsGahizDgbu7oqFH/xVtzIqMd/s+WtnSxNxJZJpLbdT5IPdzg8EZxO3+PoKahXmwJLeNQOpKJb3/bi7Q== - dependencies: - "@typescript-eslint/tsconfig-utils" "^8.65.0" - "@typescript-eslint/types" "^8.65.0" - debug "^4.4.3" - -"@typescript-eslint/scope-manager@8.65.0": - version "8.65.0" - resolved "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.65.0.tgz" - integrity sha512-Esbl8OSYiVxBokYgWPf7VVWg/BE798wXhimnn9ML9Pt5qoDf8bfQlgjlKXR/k98+AcNzlLKYrpCcrcuZ9DZLgg== - dependencies: - "@typescript-eslint/types" "8.65.0" - "@typescript-eslint/visitor-keys" "8.65.0" - -"@typescript-eslint/tsconfig-utils@8.65.0", "@typescript-eslint/tsconfig-utils@^8.65.0": - version "8.65.0" - resolved "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.65.0.tgz" - integrity sha512-j6GzGqCiRdA7Qhur2VVmKZAkBLfnHFQfx4TaJGL9RMveZqCo48jSHHO0DTgizEnGhtWnqmbtCUSrqSkdiY/0Hg== - -"@typescript-eslint/type-utils@8.65.0": - version "8.65.0" - resolved "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.65.0.tgz" - integrity sha512-YjaZ7PRI5qY7ax2L3PbvX0rRyGtipAReCWs0mhhDBHjH/vl0g0BonaGXrKdKpMbIIsMIwDgbk/xzkBTyAltS5g== - dependencies: - "@typescript-eslint/types" "8.65.0" - "@typescript-eslint/typescript-estree" "8.65.0" - "@typescript-eslint/utils" "8.65.0" - debug "^4.4.3" - ts-api-utils "^2.5.0" - -"@typescript-eslint/types@8.65.0", "@typescript-eslint/types@^8.65.0": - version "8.65.0" - resolved "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.65.0.tgz" - integrity sha512-JSSwWNy+H0E/01jJEM+hrX6N0OFDzFzeIhHFSAS01tlVaevpG8cFyYRPhS5yjGOvBUx3sqQHVMjCL1CAZZMxBg== - -"@typescript-eslint/typescript-estree@8.65.0": - version "8.65.0" - resolved "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.65.0.tgz" - integrity sha512-JboAE2swaYt4tb1fHhHTABE2K+OLy09XfcTbhnk4Pw96f9dd2e9iYsJ28gBggHlo5z5x1rkyWvcPoTuNTd4oGg== - dependencies: - "@typescript-eslint/project-service" "8.65.0" - "@typescript-eslint/tsconfig-utils" "8.65.0" - "@typescript-eslint/types" "8.65.0" - "@typescript-eslint/visitor-keys" "8.65.0" - debug "^4.4.3" - minimatch "^10.2.2" - semver "^7.7.3" - tinyglobby "^0.2.15" - ts-api-utils "^2.5.0" - -"@typescript-eslint/utils@8.65.0": - version "8.65.0" - resolved "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.65.0.tgz" - integrity sha512-gXiwIHsYreboxeJucHKPvgwl7dXt50mF8s1/c00cP/WoVTyWKFdtfhRWwZiXYFU5H2O8vVoSLNrexFZjYS/SGA== - dependencies: - "@eslint-community/eslint-utils" "^4.9.1" - "@typescript-eslint/scope-manager" "8.65.0" - "@typescript-eslint/types" "8.65.0" - "@typescript-eslint/typescript-estree" "8.65.0" - -"@typescript-eslint/visitor-keys@8.65.0": - version "8.65.0" - resolved "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.65.0.tgz" - integrity sha512-8C71BQkGjiMmXtop7pHVJu1l2NNShFdkCyD6a2ezzs5vU/L3LRtb69EtcteFwz0mYMPzIgOw0n6OV4VBUWZd7A== - dependencies: - "@typescript-eslint/types" "8.65.0" - eslint-visitor-keys "^5.0.0" - -"@vitest/expect@4.1.10": - version "4.1.10" - resolved "https://registry.npmjs.org/@vitest/expect/-/expect-4.1.10.tgz" - integrity sha512-YsCn+qAk1GWjQOWFEsEcL2gNQ0zmVmQu3T03qP6UyjhtmdtwtbuI+DASn/7iQB3HGTXkdBwGddzxPlmiql5vlA== - dependencies: - "@standard-schema/spec" "^1.1.0" - "@types/chai" "^5.2.2" - "@vitest/spy" "4.1.10" - "@vitest/utils" "4.1.10" - chai "^6.2.2" - tinyrainbow "^3.1.0" - -"@vitest/mocker@4.1.10": - version "4.1.10" - resolved "https://registry.npmjs.org/@vitest/mocker/-/mocker-4.1.10.tgz" - integrity sha512-v0xaezt+DKEmKfaxg133ldzADrwLGd7Ze1MfQQTYfvs8OqZIwbxyxaYURivwV7sWy5fqn3rH5uOrSp07bp44Ow== - dependencies: - "@vitest/spy" "4.1.10" - estree-walker "^3.0.3" - magic-string "^0.30.21" - -"@vitest/pretty-format@4.1.10": - version "4.1.10" - resolved "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-4.1.10.tgz" - integrity sha512-W1HsjSH4MXQ9YfmmhLAoIYf1HRfekQCGngeIgcei6MP5QQGWUe0gkopdZQaVCFO+JDJMrAJGwa5pRpNpvy4P8Q== - dependencies: - tinyrainbow "^3.1.0" - -"@vitest/runner@4.1.10": - version "4.1.10" - resolved "https://registry.npmjs.org/@vitest/runner/-/runner-4.1.10.tgz" - integrity sha512-IKI6kpIH+LmpROplyLwBBaCfMgOZOMsygVa6BARD6ahA04VRuJSa6OaVG7kRvSEMD870Vd91rSSw0eegtWyLGg== - dependencies: - "@vitest/utils" "4.1.10" - pathe "^2.0.3" - -"@vitest/snapshot@4.1.10": - version "4.1.10" - resolved "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-4.1.10.tgz" - integrity sha512-xRkfOT1qpTAi/Ti4Y1LtfRc3kEuqxGw59eN2jN9pRWMtS/XDevekhcFSqvQqjUNGksfjMJu3Y+oJ+4Ypn2OaJw== - dependencies: - "@vitest/pretty-format" "4.1.10" - "@vitest/utils" "4.1.10" - magic-string "^0.30.21" - pathe "^2.0.3" - -"@vitest/spy@4.1.10": - version "4.1.10" - resolved "https://registry.npmjs.org/@vitest/spy/-/spy-4.1.10.tgz" - integrity sha512-PLf/Ugvoq5wO/b4rwYCR1h2PSIdXz7wnkQFMiUpLdtM7l6pqVFcQIBEHyT1+l+cj7mNwAfZHzqXqDyjvOuwbDw== - -"@vitest/utils@4.1.10": - version "4.1.10" - resolved "https://registry.npmjs.org/@vitest/utils/-/utils-4.1.10.tgz" - integrity sha512-fy9am/HWxbaGt/Sawrp90vt6Y6jQwf1RX77cz3uwoJwJVMli/e1IEwRPnMNJ7vKfPTwo0diXifkpPvwH9v7nGA== - dependencies: - "@vitest/pretty-format" "4.1.10" - convert-source-map "^2.0.0" - tinyrainbow "^3.1.0" - -"@webgpu/types@*": - version "0.1.71" - resolved "https://registry.npmjs.org/@webgpu/types/-/types-0.1.71.tgz" - integrity sha512-mMy8/ODcKhab808co15eW+yN+HgXoQxRQHTiBV9Mrvl1r0ufnid7YOcI+gi4eUWSWl9ezD6TW2KXccrL8HCh2A== - -acorn-jsx@^5.3.2: - version "5.3.2" - resolved "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz" - integrity sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ== - -acorn@^8.15.0: - version "8.17.0" - resolved "https://registry.npmjs.org/acorn/-/acorn-8.17.0.tgz" - integrity sha512-xRQbDb9BnwDafYNn6Vwl839DYVjqXYb1XVGtWAZ1kcDc6iwAL4hg3B1dZlRiuENFeO2H53gFG3in621AdERVAg== - -ajv@^6.14.0: - version "6.15.0" - resolved "https://registry.npmjs.org/ajv/-/ajv-6.15.0.tgz" - integrity sha512-fgFx7Hfoq60ytK2c7DhnF8jIvzYgOMxfugjLOSMHjLIPgenqa7S7oaagATUq99mV6IYvN2tRmC0wnTYX6iPbMw== - 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" - -ansi-styles@^4.1.0: - version "4.3.0" - resolved "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz" - integrity sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg== - dependencies: - color-convert "^2.0.1" - -argparse@^2.0.1: - version "2.0.1" - resolved "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz" - integrity sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q== - -array-buffer-byte-length@^1.0.1, array-buffer-byte-length@^1.0.2: - version "1.0.2" - resolved "https://registry.npmjs.org/array-buffer-byte-length/-/array-buffer-byte-length-1.0.2.tgz" - integrity sha512-LHE+8BuR7RYGDKvnrmcuSq3tDcKv9OFEXQt/HpbZhY7V6h0zlUXutnAD82GiFx9rdieCMjkvtcsPqBwgUl1Iiw== - dependencies: - call-bound "^1.0.3" - is-array-buffer "^3.0.5" - -array-includes@^3.1.6, array-includes@^3.1.8, array-includes@^3.1.9: - version "3.1.9" - resolved "https://registry.npmjs.org/array-includes/-/array-includes-3.1.9.tgz" - integrity sha512-FmeCCAenzH0KH381SPT5FZmiA/TmpndpcaShhfgEN9eCVjnFBqq3l1xrI42y8+PPLI6hypzou4GXw00WHmPBLQ== - dependencies: - call-bind "^1.0.8" - call-bound "^1.0.4" - define-properties "^1.2.1" - es-abstract "^1.24.0" - es-object-atoms "^1.1.1" - get-intrinsic "^1.3.0" - is-string "^1.1.1" - math-intrinsics "^1.1.0" - -array.prototype.findlast@^1.2.5: - version "1.2.5" - resolved "https://registry.npmjs.org/array.prototype.findlast/-/array.prototype.findlast-1.2.5.tgz" - integrity sha512-CVvd6FHg1Z3POpBLxO6E6zr+rSKEQ9L6rZHAaY7lLfhKsWYUBBOuMs0e9o24oopj6H+geRCX0YJ+TJLBK2eHyQ== - dependencies: - call-bind "^1.0.7" - define-properties "^1.2.1" - es-abstract "^1.23.2" - es-errors "^1.3.0" - es-object-atoms "^1.0.0" - es-shim-unscopables "^1.0.2" - -array.prototype.findlastindex@^1.2.6: - version "1.2.6" - resolved "https://registry.npmjs.org/array.prototype.findlastindex/-/array.prototype.findlastindex-1.2.6.tgz" - integrity sha512-F/TKATkzseUExPlfvmwQKGITM3DGTK+vkAsCZoDc5daVygbJBnjEUCbgkAvVFsgfXfX4YIqZ/27G3k3tdXrTxQ== - dependencies: - call-bind "^1.0.8" - call-bound "^1.0.4" - define-properties "^1.2.1" - es-abstract "^1.23.9" - es-errors "^1.3.0" - es-object-atoms "^1.1.1" - es-shim-unscopables "^1.1.0" - -array.prototype.flat@^1.3.1, array.prototype.flat@^1.3.3: - version "1.3.3" - resolved "https://registry.npmjs.org/array.prototype.flat/-/array.prototype.flat-1.3.3.tgz" - integrity sha512-rwG/ja1neyLqCuGZ5YYrznA62D4mZXg0i1cIskIUKSiqF3Cje9/wXAls9B9s1Wa2fomMsIv8czB8jZcPmxCXFg== - dependencies: - call-bind "^1.0.8" - define-properties "^1.2.1" - es-abstract "^1.23.5" - es-shim-unscopables "^1.0.2" - -array.prototype.flatmap@^1.3.3: - version "1.3.3" - resolved "https://registry.npmjs.org/array.prototype.flatmap/-/array.prototype.flatmap-1.3.3.tgz" - integrity sha512-Y7Wt51eKJSyi80hFrJCePGGNo5ktJCslFuboqJsbf57CCPcm5zztluPlc4/aD8sWsKvlwatezpV4U1efk8kpjg== - dependencies: - call-bind "^1.0.8" - define-properties "^1.2.1" - es-abstract "^1.23.5" - es-shim-unscopables "^1.0.2" - -array.prototype.tosorted@^1.1.4: - version "1.1.4" - resolved "https://registry.npmjs.org/array.prototype.tosorted/-/array.prototype.tosorted-1.1.4.tgz" - integrity sha512-p6Fx8B7b7ZhL/gmUsAy0D15WhvDccw3mnGNbZpi3pmeJdxtWsj2jEaI4Y6oo3XiHfzuSgPwKc04MYt6KgvC/wA== - dependencies: - call-bind "^1.0.7" - define-properties "^1.2.1" - es-abstract "^1.23.3" - es-errors "^1.3.0" - es-shim-unscopables "^1.0.2" - -arraybuffer.prototype.slice@^1.0.4: - version "1.0.4" - resolved "https://registry.npmjs.org/arraybuffer.prototype.slice/-/arraybuffer.prototype.slice-1.0.4.tgz" - integrity sha512-BNoCY6SXXPQ7gF2opIP4GBE+Xw7U+pHMYKuzjgCN3GwiaIR09UUeKfheyIry77QtrCBlC0KK0q5/TER/tYh3PQ== - dependencies: - array-buffer-byte-length "^1.0.1" - call-bind "^1.0.8" - define-properties "^1.2.1" - es-abstract "^1.23.5" - es-errors "^1.3.0" - get-intrinsic "^1.2.6" - is-array-buffer "^3.0.4" - -assertion-error@^2.0.1: - version "2.0.1" - resolved "https://registry.npmjs.org/assertion-error/-/assertion-error-2.0.1.tgz" - integrity sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA== - -async-function@^1.0.0: - version "1.0.0" - resolved "https://registry.npmjs.org/async-function/-/async-function-1.0.0.tgz" - integrity sha512-hsU18Ae8CDTR6Kgu9DYf0EbCr/a5iGL0rytQDobUcdpYOKokk8LEjVphnXkDkgpi0wYVsqrXuP0bZxJaTqdgoA== - -available-typed-arrays@^1.0.7: - version "1.0.7" - resolved "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.7.tgz" - integrity sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ== - dependencies: - possible-typed-array-names "^1.0.0" - -balanced-match@^1.0.0: - version "1.0.2" - resolved "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz" - integrity sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw== - -balanced-match@^4.0.2: - version "4.0.4" - resolved "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.4.tgz" - integrity sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA== - -base64-js@^1.3.1, base64-js@^1.5.1: - version "1.5.1" - resolved "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz" - integrity sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA== - -baseline-browser-mapping@^2.10.44: - version "2.11.1" - resolved "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.11.1.tgz" - integrity sha512-HYXq73DDpCtNzOmrFsm9eSwCvWCql0RzqjpDzXN9EadiLJ4DNat0nsZ/Bzmy+Ud12mb4/zKDY0cQ805ZzN+i0A== - -brace-expansion@^1.1.7: - version "1.1.16" - resolved "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.16.tgz" - integrity sha512-IDw48K2/2kRkg9LdJxurvq3lV3aBgq0REY89duEqFRthjlPdXHKMj7EnQOXVckxzgisinf3nHfrcE2FufFLXMw== - dependencies: - balanced-match "^1.0.0" - concat-map "0.0.1" - -brace-expansion@^5.0.5: - version "5.0.7" - resolved "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.7.tgz" - integrity sha512-7oFy703dxfY3/NLxC1fh2SUCQ0H9rmAY+5EpDVfXjUTTs+HEwR2nYaqLv+GWcTsumwxPfiz6CzCNkwXwBUwqCA== - dependencies: - balanced-match "^4.0.2" - -browserslist@^4.24.0: - version "4.28.7" - resolved "https://registry.npmjs.org/browserslist/-/browserslist-4.28.7.tgz" - integrity sha512-JxV13hNrFxqjOc8alRbq9dK1MM79NEXYpma2B2J4wAtpWS5zIEIKqWPGCl7N4o7Uc7B7itylh7SuDujATRyyTw== - dependencies: - baseline-browser-mapping "^2.10.44" - caniuse-lite "^1.0.30001806" - electron-to-chromium "^1.5.393" - node-releases "^2.0.51" - update-browserslist-db "^1.2.3" - -buffer@^6.0.3: - version "6.0.3" - resolved "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz" - integrity sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA== - dependencies: - base64-js "^1.3.1" - ieee754 "^1.2.1" - -call-bind-apply-helpers@^1.0.1, call-bind-apply-helpers@^1.0.2: - version "1.0.2" - resolved "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz" - integrity sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ== - dependencies: - es-errors "^1.3.0" - function-bind "^1.1.2" - -call-bind@^1.0.7, call-bind@^1.0.8, call-bind@^1.0.9: - version "1.0.9" - resolved "https://registry.npmjs.org/call-bind/-/call-bind-1.0.9.tgz" - integrity sha512-a/hy+pNsFUTR+Iz8TCJvXudKVLAnz/DyeSUo10I5yvFDQJBFU2s9uqQpoSrJlroHUKoKqzg+epxyP9lqFdzfBQ== - dependencies: - call-bind-apply-helpers "^1.0.2" - es-define-property "^1.0.1" - get-intrinsic "^1.3.0" - set-function-length "^1.2.2" - -call-bound@^1.0.2, call-bound@^1.0.3, call-bound@^1.0.4: - version "1.0.4" - resolved "https://registry.npmjs.org/call-bound/-/call-bound-1.0.4.tgz" - integrity sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg== - dependencies: - call-bind-apply-helpers "^1.0.2" - get-intrinsic "^1.3.0" - -callsites@^3.0.0: - version "3.1.0" - resolved "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz" - integrity sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ== - -caniuse-lite@^1.0.30001806: - version "1.0.30001806" - resolved "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001806.tgz" - integrity sha512-72Cuvd95zbSYPKq6Fhg8eDJRlzgWDf7/mtoZv6Qe/DYNCEBdNxoA3+rZAU2ZhGCpZlns3EssFavaZomckT5Uuw== - -chai@^6.2.2: - version "6.2.2" - resolved "https://registry.npmjs.org/chai/-/chai-6.2.2.tgz" - integrity sha512-NUPRluOfOiTKBKvWPtSD4PhFvWCqOi0BGStNWs57X9js7XGTprSmFoz5F0tWhR4WPjNeR9jXqdC7/UpSJTnlRg== - -chalk@^4.0.0: - version "4.1.2" - resolved "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz" - integrity sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA== - dependencies: - ansi-styles "^4.1.0" - supports-color "^7.1.0" - -color-convert@^2.0.1: - version "2.0.1" - resolved "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz" - integrity sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ== - dependencies: - color-name "~1.1.4" - -color-name@~1.1.4: - version "1.1.4" - resolved "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz" - integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA== - -concat-map@0.0.1: - version "0.0.1" - resolved "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz" - integrity sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg== - -convert-source-map@^2.0.0: - version "2.0.0" - resolved "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz" - integrity sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg== - -cross-spawn@^7.0.6: - version "7.0.6" - resolved "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz" - integrity sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA== - dependencies: - path-key "^3.1.0" - shebang-command "^2.0.0" - which "^2.0.1" - -csstype@^3.2.2: - version "3.2.3" - resolved "https://registry.npmjs.org/csstype/-/csstype-3.2.3.tgz" - integrity sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ== - -data-view-buffer@^1.0.2: - version "1.0.2" - resolved "https://registry.npmjs.org/data-view-buffer/-/data-view-buffer-1.0.2.tgz" - integrity sha512-EmKO5V3OLXh1rtK2wgXRansaK1/mtVdTUEiEI0W8RkvgT05kfxaH29PliLnpLP73yYO6142Q72QNa8Wx/A5CqQ== - dependencies: - call-bound "^1.0.3" - es-errors "^1.3.0" - is-data-view "^1.0.2" - -data-view-byte-length@^1.0.2: - version "1.0.2" - resolved "https://registry.npmjs.org/data-view-byte-length/-/data-view-byte-length-1.0.2.tgz" - integrity sha512-tuhGbE6CfTM9+5ANGf+oQb72Ky/0+s3xKUpHvShfiz2RxMFgFPjsXuRLBVMtvMs15awe45SRb83D6wH4ew6wlQ== - dependencies: - call-bound "^1.0.3" - es-errors "^1.3.0" - is-data-view "^1.0.2" - -data-view-byte-offset@^1.0.1: - version "1.0.1" - resolved "https://registry.npmjs.org/data-view-byte-offset/-/data-view-byte-offset-1.0.1.tgz" - integrity sha512-BS8PfmtDGnrgYdOonGZQdLZslWIeCGFP9tpan0hi1Co2Zr2NKADsvGYA8XxuG/4UWgJ6Cjtv+YJnB6MM69QGlQ== - dependencies: - call-bound "^1.0.2" - es-errors "^1.3.0" - is-data-view "^1.0.1" - -debug@^3.2.7: - version "3.2.7" - resolved "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz" - integrity sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ== - dependencies: - ms "^2.1.1" - -debug@^4.1.0, debug@^4.3.1, debug@^4.3.2, debug@^4.4.3: - version "4.4.3" - resolved "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz" - integrity sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA== - dependencies: - ms "^2.1.3" - -deep-is@^0.1.3: - version "0.1.4" - resolved "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz" - integrity sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ== - -define-data-property@^1.0.1, define-data-property@^1.1.4: - version "1.1.4" - resolved "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz" - integrity sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A== - dependencies: - es-define-property "^1.0.0" - es-errors "^1.3.0" - gopd "^1.0.1" - -define-properties@^1.1.3, define-properties@^1.2.1: - version "1.2.1" - resolved "https://registry.npmjs.org/define-properties/-/define-properties-1.2.1.tgz" - integrity sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg== - dependencies: - define-data-property "^1.0.1" - has-property-descriptors "^1.0.0" - object-keys "^1.1.1" - -detect-libc@^2.0.3: - version "2.1.2" - resolved "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.2.tgz" - integrity sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ== - -doctrine@^2.1.0: - version "2.1.0" - resolved "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz" - integrity sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw== - dependencies: - esutils "^2.0.2" - -dunder-proto@^1.0.0, dunder-proto@^1.0.1: - version "1.0.1" - resolved "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz" - integrity sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A== - dependencies: - call-bind-apply-helpers "^1.0.1" - es-errors "^1.3.0" - gopd "^1.2.0" - -electron-to-chromium@^1.5.393: - version "1.5.395" - resolved "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.395.tgz" - integrity sha512-7zt9Aw+SrmxLWLN0zhaTWZQiCdryLVrYTq5R7iZakLvi2UQPYMMsROYV/2qVCzMeCiSXHwKOU+sZ4zOVVlrtKA== - -es-abstract-get@^1.0.0: - version "1.0.0" - resolved "https://registry.npmjs.org/es-abstract-get/-/es-abstract-get-1.0.0.tgz" - integrity sha512-6PMWXpdhshVvFp+FoWYs1EvG1Nj0tvk0dZM+XcK0xMEM1czRVcP6ohqPWHy6qPagSpC8j4+p89WXlT+xXJs/fg== - dependencies: - es-errors "^1.3.0" - es-object-atoms "^1.1.2" - is-callable "^1.2.7" - object-inspect "^1.13.4" - -es-abstract@^1.17.5, es-abstract@^1.23.2, es-abstract@^1.23.3, es-abstract@^1.23.5, es-abstract@^1.23.6, es-abstract@^1.23.9, es-abstract@^1.24.0, es-abstract@^1.24.2: - version "1.24.2" - resolved "https://registry.npmjs.org/es-abstract/-/es-abstract-1.24.2.tgz" - integrity sha512-2FpH9Q5i2RRwyEP1AylXe6nYLR5OhaJTZwmlcP0dL/+JCbgg7yyEo/sEK6HeGZRf3dFpWwThaRHVApXSkW3xeg== - dependencies: - array-buffer-byte-length "^1.0.2" - arraybuffer.prototype.slice "^1.0.4" - available-typed-arrays "^1.0.7" - call-bind "^1.0.8" - call-bound "^1.0.4" - data-view-buffer "^1.0.2" - data-view-byte-length "^1.0.2" - data-view-byte-offset "^1.0.1" - es-define-property "^1.0.1" - es-errors "^1.3.0" - es-object-atoms "^1.1.1" - es-set-tostringtag "^2.1.0" - es-to-primitive "^1.3.0" - function.prototype.name "^1.1.8" - get-intrinsic "^1.3.0" - get-proto "^1.0.1" - get-symbol-description "^1.1.0" - globalthis "^1.0.4" - gopd "^1.2.0" - has-property-descriptors "^1.0.2" - has-proto "^1.2.0" - has-symbols "^1.1.0" - hasown "^2.0.2" - internal-slot "^1.1.0" - is-array-buffer "^3.0.5" - is-callable "^1.2.7" - is-data-view "^1.0.2" - is-negative-zero "^2.0.3" - is-regex "^1.2.1" - is-set "^2.0.3" - is-shared-array-buffer "^1.0.4" - is-string "^1.1.1" - is-typed-array "^1.1.15" - is-weakref "^1.1.1" - math-intrinsics "^1.1.0" - object-inspect "^1.13.4" - object-keys "^1.1.1" - object.assign "^4.1.7" - own-keys "^1.0.1" - regexp.prototype.flags "^1.5.4" - safe-array-concat "^1.1.3" - safe-push-apply "^1.0.0" - safe-regex-test "^1.1.0" - set-proto "^1.0.0" - stop-iteration-iterator "^1.1.0" - string.prototype.trim "^1.2.10" - string.prototype.trimend "^1.0.9" - string.prototype.trimstart "^1.0.8" - typed-array-buffer "^1.0.3" - typed-array-byte-length "^1.0.3" - typed-array-byte-offset "^1.0.4" - typed-array-length "^1.0.7" - unbox-primitive "^1.1.0" - which-typed-array "^1.1.19" - -es-define-property@^1.0.0, es-define-property@^1.0.1: - version "1.0.1" - resolved "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz" - integrity sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g== - -es-errors@^1.3.0: - version "1.3.0" - resolved "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz" - integrity sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw== - -es-iterator-helpers@^1.2.1: - version "1.4.0" - resolved "https://registry.npmjs.org/es-iterator-helpers/-/es-iterator-helpers-1.4.0.tgz" - integrity sha512-c/A0P0oxkACDc+cKWw8evLXK83oBKgn0qPOqCYT4x9uolpCIJAcYvJC9QYKNDRPsTeGyCrQ326jrvgZWdCdK5Q== - dependencies: - call-bind "^1.0.9" - call-bound "^1.0.4" - define-properties "^1.2.1" - es-abstract "^1.24.2" - es-errors "^1.3.0" - es-set-tostringtag "^2.1.0" - function-bind "^1.1.2" - get-intrinsic "^1.3.0" - globalthis "^1.0.4" - gopd "^1.2.0" - has-property-descriptors "^1.0.2" - has-proto "^1.2.0" - has-symbols "^1.1.0" - internal-slot "^1.1.0" - iterator.prototype "^1.1.5" - math-intrinsics "^1.1.0" - -es-module-lexer@^2.0.0: - version "2.3.1" - resolved "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-2.3.1.tgz" - integrity sha512-shc1dbU90Yl/xq1QrC7QRtfcwURZuVRfPhZbDoldJ1cn1gzDvBaBWlv0eFolj5+0znnPJz5TXLxsN77X/12KTA== - -es-object-atoms@^1.0.0, es-object-atoms@^1.1.1, es-object-atoms@^1.1.2: - version "1.1.2" - resolved "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.2.tgz" - integrity sha512-HWcBoN6NileqtSydK2FqHbS/LoDd2pqrnQHLyJzBj4kOp/ky2MWMN694xOfkK8/SnUsW2DH7EfyVlydKCsm1Zw== - dependencies: - es-errors "^1.3.0" - -es-set-tostringtag@^2.1.0: - version "2.1.0" - resolved "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz" - integrity sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA== - dependencies: - es-errors "^1.3.0" - get-intrinsic "^1.2.6" - has-tostringtag "^1.0.2" - hasown "^2.0.2" - -es-shim-unscopables@^1.0.2, es-shim-unscopables@^1.1.0: - version "1.1.0" - resolved "https://registry.npmjs.org/es-shim-unscopables/-/es-shim-unscopables-1.1.0.tgz" - integrity sha512-d9T8ucsEhh8Bi1woXCf+TIKDIROLG5WCkxg8geBCbvk22kzwC5G2OnXVMO6FUsvQlgUUXQ2itephWDLqDzbeCw== - dependencies: - hasown "^2.0.2" - -es-to-primitive@^1.3.0: - version "1.3.4" - resolved "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.3.4.tgz" - integrity sha512-yPDz7wqpg1/mmHLmS3tcfTfbw5f1eryXvyghYBffGdERwe+mV7ZcWzTR8LR17Kvqt3qfPurjlonmnq3MKXIOXw== - dependencies: - es-abstract-get "^1.0.0" - es-define-property "^1.0.1" - es-errors "^1.3.0" - is-callable "^1.2.7" - is-date-object "^1.1.0" - is-symbol "^1.1.1" - -escalade@^3.2.0: - version "3.2.0" - resolved "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz" - integrity sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA== - -escape-string-regexp@^4.0.0: - version "4.0.0" - resolved "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz" - integrity sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA== - -eslint-config-prettier@^10.1.8: - version "10.1.8" - resolved "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-10.1.8.tgz" - integrity sha512-82GZUjRS0p/jganf6q1rEO25VSoHH0hKPCTrgillPjdI/3bgBhAE1QzHrHTizjpRvy6pGAvKjDJtk2pF9NDq8w== - -eslint-import-resolver-alias@^1.1.2: - version "1.1.2" - resolved "https://registry.npmjs.org/eslint-import-resolver-alias/-/eslint-import-resolver-alias-1.1.2.tgz" - integrity sha512-WdviM1Eu834zsfjHtcGHtGfcu+F30Od3V7I9Fi57uhBEwPkjDcii7/yW8jAT+gOhn4P/vOxxNAXbFAKsrrc15w== - -eslint-import-resolver-node@^0.3.9: - version "0.3.10" - resolved "https://registry.npmjs.org/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.10.tgz" - integrity sha512-tRrKqFyCaKict5hOd244sL6EQFNycnMQnBe+j8uqGNXYzsImGbGUU4ibtoaBmv5FLwJwcFJNeg1GeVjQfbMrDQ== - dependencies: - debug "^3.2.7" - is-core-module "^2.16.1" - resolve "^2.0.0-next.6" - -eslint-module-utils@^2.12.1: - version "2.14.0" - resolved "https://registry.npmjs.org/eslint-module-utils/-/eslint-module-utils-2.14.0.tgz" - integrity sha512-W2WCRZ9Dqntd+2u8jJcVMV2PKulc6RdLgUUoh/yQr3uB6lo/ZOeGx11sv60/8S4QFFKNslAlWhr9u0Ef7ZW6Ig== - dependencies: - debug "^3.2.7" - -eslint-plugin-import@^2.32.0: - version "2.32.0" - resolved "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.32.0.tgz" - integrity sha512-whOE1HFo/qJDyX4SnXzP4N6zOWn79WhnCUY/iDR0mPfQZO8wcYE4JClzI2oZrhBnnMUCBCHZhO6VQyoBU95mZA== - dependencies: - "@rtsao/scc" "^1.1.0" - array-includes "^3.1.9" - array.prototype.findlastindex "^1.2.6" - array.prototype.flat "^1.3.3" - array.prototype.flatmap "^1.3.3" - debug "^3.2.7" - doctrine "^2.1.0" - eslint-import-resolver-node "^0.3.9" - eslint-module-utils "^2.12.1" - hasown "^2.0.2" - is-core-module "^2.16.1" - is-glob "^4.0.3" - minimatch "^3.1.2" - object.fromentries "^2.0.8" - object.groupby "^1.0.3" - object.values "^1.2.1" - semver "^6.3.1" - string.prototype.trimend "^1.0.9" - tsconfig-paths "^3.15.0" - -eslint-plugin-prettier@^5.5.6: - version "5.5.6" - resolved "https://registry.npmjs.org/eslint-plugin-prettier/-/eslint-plugin-prettier-5.5.6.tgz" - integrity sha512-ifetmTcxWfz+4qRW3pH/ujdTq2jQIj59AxJMIN26K5avYgU8dxycUETQonWiW+wPrYXA0j3Try0l1CnwVQtDqQ== - dependencies: - prettier-linter-helpers "^1.0.1" - synckit "^0.11.13" - -eslint-plugin-react-hooks@^7.1.1: - version "7.1.1" - resolved "https://registry.npmjs.org/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-7.1.1.tgz" - integrity sha512-f2I7Gw6JbvCexzIInuSbZpfdQ44D7iqdWX01FKLvrPgqxoE7oMj8clOfto8U6vYiz4yd5oKu39rRSVOe1zRu0g== - dependencies: - "@babel/core" "^7.24.4" - "@babel/parser" "^7.24.4" - hermes-parser "^0.25.1" - zod "^3.25.0 || ^4.0.0" - zod-validation-error "^3.5.0 || ^4.0.0" - -eslint-plugin-react@^7.37.5: - version "7.37.5" - resolved "https://registry.npmjs.org/eslint-plugin-react/-/eslint-plugin-react-7.37.5.tgz" - integrity sha512-Qteup0SqU15kdocexFNAJMvCJEfa2xUKNV4CC1xsVMrIIqEy3SQ/rqyxCWNzfrd3/ldy6HMlD2e0JDVpDg2qIA== - dependencies: - array-includes "^3.1.8" - array.prototype.findlast "^1.2.5" - array.prototype.flatmap "^1.3.3" - array.prototype.tosorted "^1.1.4" - doctrine "^2.1.0" - es-iterator-helpers "^1.2.1" - estraverse "^5.3.0" - hasown "^2.0.2" - jsx-ast-utils "^2.4.1 || ^3.0.0" - minimatch "^3.1.2" - object.entries "^1.1.9" - object.fromentries "^2.0.8" - object.values "^1.2.1" - prop-types "^15.8.1" - resolve "^2.0.0-next.5" - semver "^6.3.1" - string.prototype.matchall "^4.0.12" - string.prototype.repeat "^1.0.0" - -eslint-scope@^8.4.0: - version "8.4.0" - resolved "https://registry.npmjs.org/eslint-scope/-/eslint-scope-8.4.0.tgz" - integrity sha512-sNXOfKCn74rt8RICKMvJS7XKV/Xk9kA7DyJr8mJik3S7Cwgy3qlkkmyS2uQB3jiJg6VNdZd/pDBJu0nvG2NlTg== - dependencies: - esrecurse "^4.3.0" - estraverse "^5.2.0" - -eslint-visitor-keys@^3.4.3: - version "3.4.3" - resolved "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz" - integrity sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag== - -eslint-visitor-keys@^4.2.1: - version "4.2.1" - resolved "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.1.tgz" - integrity sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ== - -eslint-visitor-keys@^5.0.0: - version "5.0.1" - resolved "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-5.0.1.tgz" - integrity sha512-tD40eHxA35h0PEIZNeIjkHoDR4YjjJp34biM0mDvplBe//mB+IHCqHDGV7pxF+7MklTvighcCPPZC7ynWyjdTA== - -eslint@^9.0.0: - version "9.39.5" - resolved "https://registry.npmjs.org/eslint/-/eslint-9.39.5.tgz" - integrity sha512-DgZS62aPLXKlnxILS/AYCoRvHaZeXceIzlXPkkGGzJWSow1aEk0lbTlxUSlyjC8jcaKxAdOnTDz+o1JFSBsyjw== - dependencies: - "@eslint-community/eslint-utils" "^4.8.0" - "@eslint-community/regexpp" "^4.12.1" - "@eslint/config-array" "^0.21.2" - "@eslint/config-helpers" "^0.4.2" - "@eslint/core" "^0.17.0" - "@eslint/eslintrc" "^3.3.6" - "@eslint/js" "9.39.5" - "@eslint/plugin-kit" "^0.4.1" - "@humanfs/node" "^0.16.6" - "@humanwhocodes/module-importer" "^1.0.1" - "@humanwhocodes/retry" "^0.4.2" - "@types/estree" "^1.0.6" - ajv "^6.14.0" - chalk "^4.0.0" - cross-spawn "^7.0.6" - debug "^4.3.2" - escape-string-regexp "^4.0.0" - eslint-scope "^8.4.0" - eslint-visitor-keys "^4.2.1" - espree "^10.4.0" - esquery "^1.5.0" - esutils "^2.0.2" - fast-deep-equal "^3.1.3" - file-entry-cache "^8.0.0" - find-up "^5.0.0" - glob-parent "^6.0.2" - ignore "^5.2.0" - imurmurhash "^0.1.4" - is-glob "^4.0.0" - json-stable-stringify-without-jsonify "^1.0.1" - lodash.merge "^4.6.2" - minimatch "^3.1.5" - natural-compare "^1.4.0" - optionator "^0.9.3" - -espree@^10.0.1, espree@^10.4.0: - version "10.4.0" - resolved "https://registry.npmjs.org/espree/-/espree-10.4.0.tgz" - integrity sha512-j6PAQ2uUr79PZhBjP5C5fhl8e39FmRnOjsD5lGnWrFU8i2G776tBK7+nP8KuQUTTyAZUwfQqXAgrVH5MbH9CYQ== - dependencies: - acorn "^8.15.0" - acorn-jsx "^5.3.2" - eslint-visitor-keys "^4.2.1" - -esquery@^1.5.0: - version "1.7.0" - resolved "https://registry.npmjs.org/esquery/-/esquery-1.7.0.tgz" - integrity sha512-Ap6G0WQwcU/LHsvLwON1fAQX9Zp0A2Y6Y/cJBl9r/JbW90Zyg4/zbG6zzKa2OTALELarYHmKu0GhpM5EO+7T0g== - dependencies: - estraverse "^5.1.0" - -esrecurse@^4.3.0: - version "4.3.0" - resolved "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz" - integrity sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag== - dependencies: - estraverse "^5.2.0" - -estraverse@^5.1.0, estraverse@^5.2.0, estraverse@^5.3.0: - version "5.3.0" - resolved "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz" - integrity sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA== - -estree-walker@^3.0.3: - version "3.0.3" - resolved "https://registry.npmjs.org/estree-walker/-/estree-walker-3.0.3.tgz" - integrity sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g== - dependencies: - "@types/estree" "^1.0.0" - -esutils@^2.0.2: - version "2.0.3" - resolved "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz" - integrity sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g== - -expect-type@^1.3.0: - version "1.4.0" - resolved "https://registry.npmjs.org/expect-type/-/expect-type-1.4.0.tgz" - integrity sha512-KfYbmpRm0VbLjEvVa9yGwCi9GI34xvi7A/HXYWQO65CSD2u3MczUJSuwXKFIxlGsgBQizV9q5J9NHj4VG0n+pA== - -fast-deep-equal@^3.1.1, fast-deep-equal@^3.1.3: - version "3.1.3" - resolved "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz" - integrity sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q== - -fast-diff@^1.1.2: - version "1.3.0" - resolved "https://registry.npmjs.org/fast-diff/-/fast-diff-1.3.0.tgz" - integrity sha512-VxPP4NqbUjj6MaAOafWeUn2cXWLcCtljklUtZf0Ind4XQ+QPtmA0b18zZy0jIQx+ExRVCR/ZQpBmik5lXshNsw== - -fast-json-stable-stringify@^2.0.0: - version "2.1.0" - resolved "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz" - integrity sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw== - -fast-levenshtein@^2.0.6: - version "2.0.6" - resolved "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz" - integrity sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw== - -fdir@^6.5.0: - version "6.5.0" - resolved "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz" - integrity sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg== - -fflate@~0.8.2: - version "0.8.3" - resolved "https://registry.npmjs.org/fflate/-/fflate-0.8.3.tgz" - integrity sha512-tbZNuJrLwGUp3zshBtdy4W+ORxZuIh8a5ilyIEQDC5rY1f3U20JMry0Ll3WBzU58EZKsEuJFXhb5gwv8CsPvgA== - -file-entry-cache@^8.0.0: - version "8.0.0" - resolved "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-8.0.0.tgz" - integrity sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ== - dependencies: - flat-cache "^4.0.0" - -find-up@^5.0.0: - version "5.0.0" - resolved "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz" - integrity sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng== - dependencies: - locate-path "^6.0.0" - path-exists "^4.0.0" - -flat-cache@^4.0.0: - version "4.0.1" - resolved "https://registry.npmjs.org/flat-cache/-/flat-cache-4.0.1.tgz" - integrity sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw== - dependencies: - flatted "^3.2.9" - keyv "^4.5.4" - -flatted@^3.2.9: - version "3.4.3" - resolved "https://registry.npmjs.org/flatted/-/flatted-3.4.3.tgz" - integrity sha512-/zipXxyO6rGvuNGDiULY9MvEGSkb2gaG4GGH4ygMi0ZZzyMHdUZBmntJmx5x1G2VuPytCwGN4xsJP6cw+sK+vQ== - -for-each@^0.3.3, for-each@^0.3.5: - version "0.3.5" - resolved "https://registry.npmjs.org/for-each/-/for-each-0.3.5.tgz" - integrity sha512-dKx12eRCVIzqCxFGplyFKJMPvLEWgmNtUrpTiJIR5u97zEhRG8ySrtboPHZXx7daLxQVrl643cTzbab2tkQjxg== - dependencies: - is-callable "^1.2.7" - -fsevents@~2.3.3: - version "2.3.3" - resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.3.3.tgz#cac6407785d03675a2a5e1a5305c697b347d90d6" - integrity sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw== - -function-bind@^1.1.2: - version "1.1.2" - resolved "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz" - integrity sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA== - -function.prototype.name@^1.1.6, function.prototype.name@^1.1.8: - version "1.2.0" - resolved "https://registry.npmjs.org/function.prototype.name/-/function.prototype.name-1.2.0.tgz" - integrity sha512-jObKIik1P2QjPHP5nz5BaOtUlfgS0fWo8IUByNXkM+o+02sJOi94em77GwJKQSJ3gfPHdgzLNrHc1uokV4P/ew== - dependencies: - call-bind "^1.0.9" - call-bound "^1.0.4" - es-define-property "^1.0.1" - es-errors "^1.3.0" - functions-have-names "^1.2.3" - has-property-descriptors "^1.0.2" - hasown "^2.0.4" - is-callable "^1.2.7" - is-document.all "^1.0.0" - -functions-have-names@^1.2.3: - version "1.2.3" - resolved "https://registry.npmjs.org/functions-have-names/-/functions-have-names-1.2.3.tgz" - integrity sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ== - -generator-function@^2.0.0: - version "2.0.1" - resolved "https://registry.npmjs.org/generator-function/-/generator-function-2.0.1.tgz" - integrity sha512-SFdFmIJi+ybC0vjlHN0ZGVGHc3lgE0DxPAT0djjVg+kjOnSqclqmj0KQ7ykTOLP6YxoqOvuAODGdcHJn+43q3g== - -gensync@^1.0.0-beta.2: - version "1.0.0-beta.2" - resolved "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz" - integrity sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg== - -get-intrinsic@^1.2.4, get-intrinsic@^1.2.5, get-intrinsic@^1.2.6, get-intrinsic@^1.2.7, get-intrinsic@^1.3.0: - version "1.3.0" - resolved "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz" - integrity sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ== - dependencies: - call-bind-apply-helpers "^1.0.2" - es-define-property "^1.0.1" - es-errors "^1.3.0" - es-object-atoms "^1.1.1" - function-bind "^1.1.2" - get-proto "^1.0.1" - gopd "^1.2.0" - has-symbols "^1.1.0" - hasown "^2.0.2" - math-intrinsics "^1.1.0" - -get-proto@^1.0.0, get-proto@^1.0.1: - version "1.0.1" - resolved "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz" - integrity sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g== - dependencies: - dunder-proto "^1.0.1" - es-object-atoms "^1.0.0" - -get-symbol-description@^1.1.0: - version "1.1.0" - resolved "https://registry.npmjs.org/get-symbol-description/-/get-symbol-description-1.1.0.tgz" - integrity sha512-w9UMqWwJxHNOvoNzSJ2oPF5wvYcvP7jUvYzhp67yEhTi17ZDBBC1z9pTdGuzjD+EFIqLSYRweZjqfiPzQ06Ebg== - dependencies: - call-bound "^1.0.3" - es-errors "^1.3.0" - get-intrinsic "^1.2.6" - -glob-parent@^6.0.2: - version "6.0.2" - resolved "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz" - integrity sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A== - dependencies: - is-glob "^4.0.3" - -globals@^14.0.0: - version "14.0.0" - resolved "https://registry.npmjs.org/globals/-/globals-14.0.0.tgz" - integrity sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ== - -globals@^17.7.0: - version "17.7.0" - resolved "https://registry.npmjs.org/globals/-/globals-17.7.0.tgz" - integrity sha512-Czmyns5dUsq4seFBR/Kdydhmo8y9kC79hiSkPn0YcGtNnYWnrgt0vjrSjx9tspoDGWm2CMarffRuLjM4xUz8xg== - -globalthis@^1.0.4: - version "1.0.4" - resolved "https://registry.npmjs.org/globalthis/-/globalthis-1.0.4.tgz" - integrity sha512-DpLKbNU4WylpxJykQujfCcwYWiV/Jhm50Goo0wrVILAv5jOr9d+H+UR3PhSCD2rCCEIg0uc+G+muBTwD54JhDQ== - dependencies: - define-properties "^1.2.1" - gopd "^1.0.1" - -gopd@^1.0.1, gopd@^1.2.0: - version "1.2.0" - resolved "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz" - integrity sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg== - -has-bigints@^1.0.2: - version "1.1.0" - resolved "https://registry.npmjs.org/has-bigints/-/has-bigints-1.1.0.tgz" - integrity sha512-R3pbpkcIqv2Pm3dUwgjclDRVmWpTJW2DcMzcIhEXEx1oh/CEMObMm3KLmRJOdvhM7o4uQBnwr8pzRK2sJWIqfg== - -has-flag@^4.0.0: - version "4.0.0" - resolved "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz" - integrity sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ== - -has-property-descriptors@^1.0.0, has-property-descriptors@^1.0.2: - version "1.0.2" - resolved "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz" - integrity sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg== - dependencies: - es-define-property "^1.0.0" - -has-proto@^1.2.0: - version "1.2.0" - resolved "https://registry.npmjs.org/has-proto/-/has-proto-1.2.0.tgz" - integrity sha512-KIL7eQPfHQRC8+XluaIw7BHUwwqL19bQn4hzNgdr+1wXoU0KKj6rufu47lhY7KbJR2C6T6+PfyN0Ea7wkSS+qQ== - dependencies: - dunder-proto "^1.0.0" - -has-symbols@^1.0.3, has-symbols@^1.1.0: - version "1.1.0" - resolved "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz" - integrity sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ== - -has-tostringtag@^1.0.2: - version "1.0.2" - resolved "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz" - integrity sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw== - dependencies: - has-symbols "^1.0.3" - -hasown@^2.0.2, hasown@^2.0.3, hasown@^2.0.4: - version "2.0.4" - resolved "https://registry.npmjs.org/hasown/-/hasown-2.0.4.tgz" - integrity sha512-T2UbfbBEF32wiepXIsMlTW9+dDYC6wMh/t/vYA4tuOMKqWz/n3vr1NFSxQiyP+zk2mXsoMA/i/7qV6LKut1t1A== - dependencies: - function-bind "^1.1.2" - -hermes-estree@0.25.1: - version "0.25.1" - resolved "https://registry.npmjs.org/hermes-estree/-/hermes-estree-0.25.1.tgz" - integrity sha512-0wUoCcLp+5Ev5pDW2OriHC2MJCbwLwuRx+gAqMTOkGKJJiBCLjtrvy4PWUGn6MIVefecRpzoOZ/UV6iGdOr+Cw== - -hermes-parser@^0.25.1: - version "0.25.1" - resolved "https://registry.npmjs.org/hermes-parser/-/hermes-parser-0.25.1.tgz" - integrity sha512-6pEjquH3rqaI6cYAXYPcz9MS4rY6R4ngRgrgfDshRptUZIc3lw0MCIJIGDj9++mfySOuPTHB4nrSW99BCvOPIA== - dependencies: - hermes-estree "0.25.1" - -ieee754@^1.2.1: - version "1.2.1" - resolved "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz" - integrity sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA== - -ignore@^5.2.0: - version "5.3.2" - resolved "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz" - integrity sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g== - -ignore@^7.0.5: - version "7.0.6" - resolved "https://registry.npmjs.org/ignore/-/ignore-7.0.6.tgz" - integrity sha512-BAg6QkE8W+TuQLrrw0Ugr7HegXduRuuj8/ti2kSOc+jz1dmx8/WNcjr6XGnq5YpDWxFwwaavqD0+jIUOKelTsw== - -import-fresh@^3.2.1: - version "3.3.1" - resolved "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.1.tgz" - integrity sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ== - dependencies: - parent-module "^1.0.0" - resolve-from "^4.0.0" - -imurmurhash@^0.1.4: - version "0.1.4" - resolved "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz" - integrity sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA== - -internal-slot@^1.1.0: - version "1.1.0" - resolved "https://registry.npmjs.org/internal-slot/-/internal-slot-1.1.0.tgz" - integrity sha512-4gd7VpWNQNB4UKKCFFVcp1AVv+FMOgs9NKzjHKusc8jTMhd5eL1NqQqOpE0KzMds804/yHlglp3uxgluOqAPLw== - dependencies: - es-errors "^1.3.0" - hasown "^2.0.2" - side-channel "^1.1.0" - -is-array-buffer@^3.0.4, is-array-buffer@^3.0.5: - version "3.0.5" - resolved "https://registry.npmjs.org/is-array-buffer/-/is-array-buffer-3.0.5.tgz" - integrity sha512-DDfANUiiG2wC1qawP66qlTugJeL5HyzMpfr8lLK+jMQirGzNod0B12cFB/9q838Ru27sBwfw78/rdoU7RERz6A== - dependencies: - call-bind "^1.0.8" - call-bound "^1.0.3" - get-intrinsic "^1.2.6" - -is-async-function@^2.0.0: - version "2.1.1" - resolved "https://registry.npmjs.org/is-async-function/-/is-async-function-2.1.1.tgz" - integrity sha512-9dgM/cZBnNvjzaMYHVoxxfPj2QXt22Ev7SuuPrs+xav0ukGB0S6d4ydZdEiM48kLx5kDV+QBPrpVnFyefL8kkQ== - dependencies: - async-function "^1.0.0" - call-bound "^1.0.3" - get-proto "^1.0.1" - has-tostringtag "^1.0.2" - safe-regex-test "^1.1.0" - -is-bigint@^1.1.0: - version "1.1.0" - resolved "https://registry.npmjs.org/is-bigint/-/is-bigint-1.1.0.tgz" - integrity sha512-n4ZT37wG78iz03xPRKJrHTdZbe3IicyucEtdRsV5yglwc3GyUfbAfpSeD0FJ41NbUNSt5wbhqfp1fS+BgnvDFQ== - dependencies: - has-bigints "^1.0.2" - -is-boolean-object@^1.2.1: - version "1.2.2" - resolved "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.2.2.tgz" - integrity sha512-wa56o2/ElJMYqjCjGkXri7it5FbebW5usLw/nPmCMs5DeZ7eziSYZhSmPRn0txqeW4LnAmQQU7FgqLpsEFKM4A== - dependencies: - call-bound "^1.0.3" - has-tostringtag "^1.0.2" - -is-callable@^1.2.7: - version "1.2.7" - resolved "https://registry.npmjs.org/is-callable/-/is-callable-1.2.7.tgz" - integrity sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA== - -is-core-module@^2.16.1, is-core-module@^2.16.2: - version "2.16.2" - resolved "https://registry.npmjs.org/is-core-module/-/is-core-module-2.16.2.tgz" - integrity sha512-evOr8xfXKxE6qSR0hSXL2r3sd7ALj8+7jQEUvPYcm5sgZFdJ+AYzT6yNmJenvIYQBgIGwfwz08sL8zoL7yq2BA== - dependencies: - hasown "^2.0.3" - -is-data-view@^1.0.1, is-data-view@^1.0.2: - version "1.0.2" - resolved "https://registry.npmjs.org/is-data-view/-/is-data-view-1.0.2.tgz" - integrity sha512-RKtWF8pGmS87i2D6gqQu/l7EYRlVdfzemCJN/P3UOs//x1QE7mfhvzHIApBTRf7axvT6DMGwSwBXYCT0nfB9xw== - dependencies: - call-bound "^1.0.2" - get-intrinsic "^1.2.6" - is-typed-array "^1.1.13" - -is-date-object@^1.1.0: - version "1.1.0" - resolved "https://registry.npmjs.org/is-date-object/-/is-date-object-1.1.0.tgz" - integrity sha512-PwwhEakHVKTdRNVOw+/Gyh0+MzlCl4R6qKvkhuvLtPMggI1WAHt9sOwZxQLSGpUaDnrdyDsomoRgNnCfKNSXXg== - dependencies: - call-bound "^1.0.2" - has-tostringtag "^1.0.2" - -is-document.all@^1.0.0: - version "1.0.0" - resolved "https://registry.npmjs.org/is-document.all/-/is-document.all-1.0.0.tgz" - integrity sha512-+XSoyS05OdBbhFuELhgTCpFNHkpBOJqtsZfUFFpe5QTw+9Sjbh8zitxhQkYAo6wV7e1Vb8cAPvpCk9jGam/82g== - dependencies: - call-bound "^1.0.4" - -is-extglob@^2.1.1: - version "2.1.1" - resolved "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz" - integrity sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ== - -is-finalizationregistry@^1.1.0: - version "1.1.1" - resolved "https://registry.npmjs.org/is-finalizationregistry/-/is-finalizationregistry-1.1.1.tgz" - integrity sha512-1pC6N8qWJbWoPtEjgcL2xyhQOP491EQjeUo3qTKcmV8YSDDJrOepfG8pcC7h/QgnQHYSv0mJ3Z/ZWxmatVrysg== - dependencies: - call-bound "^1.0.3" - -is-generator-function@^1.0.10: - version "1.1.2" - resolved "https://registry.npmjs.org/is-generator-function/-/is-generator-function-1.1.2.tgz" - integrity sha512-upqt1SkGkODW9tsGNG5mtXTXtECizwtS2kA161M+gJPc1xdb/Ax629af6YrTwcOeQHbewrPNlE5Dx7kzvXTizA== - dependencies: - call-bound "^1.0.4" - generator-function "^2.0.0" - get-proto "^1.0.1" - has-tostringtag "^1.0.2" - safe-regex-test "^1.1.0" - -is-glob@^4.0.0, is-glob@^4.0.3: - version "4.0.3" - resolved "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz" - integrity sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg== - dependencies: - is-extglob "^2.1.1" - -is-map@^2.0.3: - version "2.0.3" - resolved "https://registry.npmjs.org/is-map/-/is-map-2.0.3.tgz" - integrity sha512-1Qed0/Hr2m+YqxnM09CjA2d/i6YZNfF6R2oRAOj36eUdS6qIV/huPJNSEpKbupewFs+ZsJlxsjjPbc0/afW6Lw== - -is-negative-zero@^2.0.3: - version "2.0.3" - resolved "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.3.tgz" - integrity sha512-5KoIu2Ngpyek75jXodFvnafB6DJgr3u8uuK0LEZJjrU19DrMD3EVERaR8sjz8CCGgpZvxPl9SuE1GMVPFHx1mw== - -is-number-object@^1.1.1: - version "1.1.1" - resolved "https://registry.npmjs.org/is-number-object/-/is-number-object-1.1.1.tgz" - integrity sha512-lZhclumE1G6VYD8VHe35wFaIif+CTy5SJIi5+3y4psDgWu4wPDoBhF8NxUOinEc7pHgiTsT6MaBb92rKhhD+Xw== - dependencies: - call-bound "^1.0.3" - has-tostringtag "^1.0.2" - -is-regex@^1.2.1: - version "1.2.1" - resolved "https://registry.npmjs.org/is-regex/-/is-regex-1.2.1.tgz" - integrity sha512-MjYsKHO5O7mCsmRGxWcLWheFqN9DJ/2TmngvjKXihe6efViPqc274+Fx/4fYj/r03+ESvBdTXK0V6tA3rgez1g== - dependencies: - call-bound "^1.0.2" - gopd "^1.2.0" - has-tostringtag "^1.0.2" - hasown "^2.0.2" - -is-set@^2.0.3: - version "2.0.3" - resolved "https://registry.npmjs.org/is-set/-/is-set-2.0.3.tgz" - integrity sha512-iPAjerrse27/ygGLxw+EBR9agv9Y6uLeYVJMu+QNCoouJ1/1ri0mGrcWpfCqFZuzzx3WjtwxG098X+n4OuRkPg== - -is-shared-array-buffer@^1.0.4: - version "1.0.4" - resolved "https://registry.npmjs.org/is-shared-array-buffer/-/is-shared-array-buffer-1.0.4.tgz" - integrity sha512-ISWac8drv4ZGfwKl5slpHG9OwPNty4jOWPRIhBpxOoD+hqITiwuipOQ2bNthAzwA3B4fIjO4Nln74N0S9byq8A== - dependencies: - call-bound "^1.0.3" - -is-string@^1.1.1: - version "1.1.1" - resolved "https://registry.npmjs.org/is-string/-/is-string-1.1.1.tgz" - integrity sha512-BtEeSsoaQjlSPBemMQIrY1MY0uM6vnS1g5fmufYOtnxLGUZM2178PKbhsk7Ffv58IX+ZtcvoGwccYsh0PglkAA== - dependencies: - call-bound "^1.0.3" - has-tostringtag "^1.0.2" - -is-symbol@^1.1.1: - version "1.1.1" - resolved "https://registry.npmjs.org/is-symbol/-/is-symbol-1.1.1.tgz" - integrity sha512-9gGx6GTtCQM73BgmHQXfDmLtfjjTUDSyoxTCbp5WtoixAhfgsDirWIcVQ/IHpvI5Vgd5i/J5F7B9cN/WlVbC/w== - dependencies: - call-bound "^1.0.2" - has-symbols "^1.1.0" - safe-regex-test "^1.1.0" - -is-typed-array@^1.1.13, is-typed-array@^1.1.14, is-typed-array@^1.1.15: - version "1.1.15" - resolved "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.15.tgz" - integrity sha512-p3EcsicXjit7SaskXHs1hA91QxgTw46Fv6EFKKGS5DRFLD8yKnohjF3hxoju94b/OcMZoQukzpPpBE9uLVKzgQ== - dependencies: - which-typed-array "^1.1.16" - -is-weakmap@^2.0.2: - version "2.0.2" - resolved "https://registry.npmjs.org/is-weakmap/-/is-weakmap-2.0.2.tgz" - integrity sha512-K5pXYOm9wqY1RgjpL3YTkF39tni1XajUIkawTLUo9EZEVUFga5gSQJF8nNS7ZwJQ02y+1YCNYcMh+HIf1ZqE+w== - -is-weakref@^1.0.2, is-weakref@^1.1.1: - version "1.1.1" - resolved "https://registry.npmjs.org/is-weakref/-/is-weakref-1.1.1.tgz" - integrity sha512-6i9mGWSlqzNMEqpCp93KwRS1uUOodk2OJ6b+sq7ZPDSy2WuI5NFIxp/254TytR8ftefexkWn5xNiHUNpPOfSew== - dependencies: - call-bound "^1.0.3" - -is-weakset@^2.0.3: - version "2.0.4" - resolved "https://registry.npmjs.org/is-weakset/-/is-weakset-2.0.4.tgz" - integrity sha512-mfcwb6IzQyOKTs84CQMrOwW4gQcaTOAWJ0zzJCl2WSPDrWk/OzDaImWFH3djXhb24g4eudZfLRozAvPGw4d9hQ== - dependencies: - call-bound "^1.0.3" - get-intrinsic "^1.2.6" - -isarray@^2.0.5: - version "2.0.5" - resolved "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz" - integrity sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw== - -isexe@^2.0.0: - version "2.0.0" - resolved "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz" - integrity sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw== - -iterator.prototype@^1.1.5: - version "1.1.5" - resolved "https://registry.npmjs.org/iterator.prototype/-/iterator.prototype-1.1.5.tgz" - integrity sha512-H0dkQoCa3b2VEeKQBOxFph+JAbcrQdE7KC0UkqwpLmv2EC4P41QXP+rqo9wYodACiG5/WM5s9oDApTU8utwj9g== - dependencies: - define-data-property "^1.1.4" - es-object-atoms "^1.0.0" - get-intrinsic "^1.2.6" - get-proto "^1.0.0" - has-symbols "^1.1.0" - set-function-name "^2.0.2" - -its-fine@^2.0.0: - version "2.0.0" - resolved "https://registry.npmjs.org/its-fine/-/its-fine-2.0.0.tgz" - integrity sha512-KLViCmWx94zOvpLwSlsx6yOCeMhZYaxrJV87Po5k/FoZzcPSahvK5qJ7fYhS61sZi5ikmh2S3Hz55A2l3U69ng== - dependencies: - "@types/react-reconciler" "^0.28.9" - -"js-tokens@^3.0.0 || ^4.0.0", js-tokens@^4.0.0: - version "4.0.0" - resolved "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz" - integrity sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ== - -js-yaml@^4.3.0: - version "4.3.0" - resolved "https://registry.npmjs.org/js-yaml/-/js-yaml-4.3.0.tgz" - integrity sha512-1td788aAnnZ5qs7V2QIRl1owjtYpbKt749Y3xauqQgwIIGF/xXWz1wMTEBx5O3LK3lXLVuqXPdPxj2BoFHaW9Q== - dependencies: - argparse "^2.0.1" - -jsesc@^3.0.2: - version "3.1.0" - resolved "https://registry.npmjs.org/jsesc/-/jsesc-3.1.0.tgz" - integrity sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA== - -json-buffer@3.0.1: - version "3.0.1" - resolved "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz" - integrity sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ== - -json-schema-traverse@^0.4.1: - version "0.4.1" - resolved "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz" - integrity sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg== - -json-stable-stringify-without-jsonify@^1.0.1: - version "1.0.1" - resolved "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz" - integrity sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw== - -json5@^1.0.2: - version "1.0.2" - resolved "https://registry.npmjs.org/json5/-/json5-1.0.2.tgz" - integrity sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA== - dependencies: - minimist "^1.2.0" - -json5@^2.2.3: - version "2.2.3" - resolved "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz" - integrity sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg== - -"jsx-ast-utils@^2.4.1 || ^3.0.0": - version "3.3.5" - resolved "https://registry.npmjs.org/jsx-ast-utils/-/jsx-ast-utils-3.3.5.tgz" - integrity sha512-ZZow9HBI5O6EPgSJLUb8n2NKgmVWTwCvHGwFuJlMjvLFqlGG6pjirPhtdsseaLZjSibD8eegzmYpUZwoIlj2cQ== - dependencies: - array-includes "^3.1.6" - array.prototype.flat "^1.3.1" - object.assign "^4.1.4" - object.values "^1.1.6" - -keyv@^4.5.4: - version "4.5.4" - resolved "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz" - integrity sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw== - dependencies: - json-buffer "3.0.1" - -levn@^0.4.1: - version "0.4.1" - resolved "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz" - integrity sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ== - dependencies: - prelude-ls "^1.2.1" - type-check "~0.4.0" - -lightningcss-android-arm64@1.33.0: - version "1.33.0" - resolved "https://registry.yarnpkg.com/lightningcss-android-arm64/-/lightningcss-android-arm64-1.33.0.tgz#9a6841f88ae50fc83502903892b41af41bc2b907" - integrity sha512-gEpRTalKdosp4Bb8qWtc2iOgE5SeIHlpS1up9bFq2wAyYhl1UdTObYiHe98zEM9SQvSoqQZ1IQD0JNpg3Ml5pg== - -lightningcss-darwin-arm64@1.33.0: - version "1.33.0" - resolved "https://registry.yarnpkg.com/lightningcss-darwin-arm64/-/lightningcss-darwin-arm64-1.33.0.tgz#c0f2c31c0bfd19fa4dd3f18e957a1f1a152097d6" - integrity sha512-Sciaz8eenNTKn9b3t7+xr0ipTp9YxKQY4npwQ3mrRuL0BAVHBLyZxofhaKBAVtzmtRZ/zTyo0/to4B1uWG/Djg== - -lightningcss-darwin-x64@1.33.0: - version "1.33.0" - resolved "https://registry.yarnpkg.com/lightningcss-darwin-x64/-/lightningcss-darwin-x64-1.33.0.tgz#cb0705965acb538c6683949ce6925fb3cdf7c361" - integrity sha512-Z5UPAxzrjlWNNyGy6i65cJzzvgJ5D3T6wMvs+gWpY9d7qRhANrxqAp6LhxIgZhWEw18RfJTGcRxjuLIBr+m8XQ== - -lightningcss-freebsd-x64@1.33.0: - version "1.33.0" - resolved "https://registry.yarnpkg.com/lightningcss-freebsd-x64/-/lightningcss-freebsd-x64-1.33.0.tgz#763538828b26bab2680dadafcc84ee78b0eb502b" - integrity sha512-QQM/Ti/hQajJwCY+RiWuCZ9sdtI/XQk7nDK5vC8kkdwixezOlDgvDx7+RT+QjK6FcFT4MpsuoBnHIo/O3StRRg== - -lightningcss-linux-arm-gnueabihf@1.33.0: - version "1.33.0" - resolved "https://registry.yarnpkg.com/lightningcss-linux-arm-gnueabihf/-/lightningcss-linux-arm-gnueabihf-1.33.0.tgz#6862e3176a331aedbdec1ed352b4d7d0dd0784de" - integrity sha512-N7FVBe6iS24MlM6R/4RBTxGhQheZGs7tiQ9U32UtF75NzP5Q7xWPRqLBCKxlRQRk3rY1jCIPLzx7WzOhuUIRLQ== - -lightningcss-linux-arm64-gnu@1.33.0: - version "1.33.0" - resolved "https://registry.yarnpkg.com/lightningcss-linux-arm64-gnu/-/lightningcss-linux-arm64-gnu-1.33.0.tgz#c6a3a2ed15141daf6bdc2628930f8e39bdf473aa" - integrity sha512-j2v/itmy4HlNxlc6voKXYgBqNi0Ng2LShg4z7GufpEgs05P+2suBVyi9I6YHq5uoVFx9ETin3eCEhLVyXGQnKg== - -lightningcss-linux-arm64-musl@1.33.0: - version "1.33.0" - resolved "https://registry.yarnpkg.com/lightningcss-linux-arm64-musl/-/lightningcss-linux-arm64-musl-1.33.0.tgz#7fa1334971fc82845f9827df6ef8a0b20914bac6" - integrity sha512-yiO5ROMuYQgXbC60yjZU5CYSFZGKXL0HFATXt9mHJn1+zW55oCtMI9NfcVhYLMFDL7gV7oBPon/EmMMGg2OvtQ== - -lightningcss-linux-x64-gnu@1.33.0: - version "1.33.0" - resolved "https://registry.yarnpkg.com/lightningcss-linux-x64-gnu/-/lightningcss-linux-x64-gnu-1.33.0.tgz#8b927862ea8c2bbc6831a46509244b50d9936e55" - integrity sha512-ar+Ju7LmcN0Jo4FpL4hpFybwNG9/3A/Br5KW2n2jyODg3MEZXaDYADdemoNS+BDNfMgKvylJLj4S5tyRActuAg== - -lightningcss-linux-x64-musl@1.33.0: - version "1.33.0" - resolved "https://registry.yarnpkg.com/lightningcss-linux-x64-musl/-/lightningcss-linux-x64-musl-1.33.0.tgz#0c525bb077dfd94404c059cfe42dad797e96aeaf" - integrity sha512-RYiYbkokw0trfKqqzfF55lginwEPrD3OJDfTuJzFs1MK6iFnDenaz1fqLLtX4ITG3OktJQXOeTaw1awrBAlZPw== - -lightningcss-win32-arm64-msvc@1.33.0: - version "1.33.0" - resolved "https://registry.yarnpkg.com/lightningcss-win32-arm64-msvc/-/lightningcss-win32-arm64-msvc-1.33.0.tgz#850ee1103dac989cfab50e3ac22d1a69e394e63d" - integrity sha512-1K+MPfLSFVpphzpdbfkhlWk6wBrTObBzS2T6db10PNOZgR9GoVsAWzwNyuhUYYbTp23j+4RrncfujZ4uAzXvwA== - -lightningcss-win32-x64-msvc@1.33.0: - version "1.33.0" - resolved "https://registry.npmjs.org/lightningcss-win32-x64-msvc/-/lightningcss-win32-x64-msvc-1.33.0.tgz" - integrity sha512-OlEICDx/Xl0FqSp4bry8zFnCvGpig3Gl4gCquvYwHuqJKEC1+n9NgDniFvqHGmMv1ZkqDJrDqKKSykTDX+ehuA== - -lightningcss@^1.32.0: - version "1.33.0" - resolved "https://registry.npmjs.org/lightningcss/-/lightningcss-1.33.0.tgz" - integrity sha512-WkUDrojuJs0xkgGf2udWxa3yGBRxPtxUkB79i6aCZLRgc7PM8fZe9TosfPDcvEpQZbuFASnHYmRLBLUbmLOIIA== - dependencies: - detect-libc "^2.0.3" - optionalDependencies: - lightningcss-android-arm64 "1.33.0" - lightningcss-darwin-arm64 "1.33.0" - lightningcss-darwin-x64 "1.33.0" - lightningcss-freebsd-x64 "1.33.0" - lightningcss-linux-arm-gnueabihf "1.33.0" - lightningcss-linux-arm64-gnu "1.33.0" - lightningcss-linux-arm64-musl "1.33.0" - lightningcss-linux-x64-gnu "1.33.0" - lightningcss-linux-x64-musl "1.33.0" - lightningcss-win32-arm64-msvc "1.33.0" - lightningcss-win32-x64-msvc "1.33.0" - -locate-path@^6.0.0: - version "6.0.0" - resolved "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz" - integrity sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw== - dependencies: - p-locate "^5.0.0" - -lodash.merge@^4.6.2: - version "4.6.2" - resolved "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz" - integrity sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ== - -loose-envify@^1.4.0: - version "1.4.0" - resolved "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz" - integrity sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q== - dependencies: - js-tokens "^3.0.0 || ^4.0.0" - -lru-cache@^5.1.1: - version "5.1.1" - resolved "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz" - integrity sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w== - dependencies: - yallist "^3.0.2" - -maath@^0.10.8: - version "0.10.8" - resolved "https://registry.npmjs.org/maath/-/maath-0.10.8.tgz" - integrity sha512-tRvbDF0Pgqz+9XUa4jjfgAQ8/aPKmQdWXilFu2tMy4GWj4NOsx99HlULO4IeREfbO3a0sA145DZYyvXPkybm0g== - -magic-string@^0.30.21: - version "0.30.21" - resolved "https://registry.npmjs.org/magic-string/-/magic-string-0.30.21.tgz" - integrity sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ== - dependencies: - "@jridgewell/sourcemap-codec" "^1.5.5" - -math-intrinsics@^1.1.0: - version "1.1.0" - resolved "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz" - integrity sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g== - -meshoptimizer@~0.22.0: - version "0.22.0" - resolved "https://registry.npmjs.org/meshoptimizer/-/meshoptimizer-0.22.0.tgz" - integrity sha512-IebiK79sqIy+E4EgOr+CAw+Ke8hAspXKzBd0JdgEmPHiAwmvEj2S4h1rfvo+o/BnfEYd/jAOg5IeeIjzlzSnDg== - -minimatch@^10.2.2: - version "10.2.5" - resolved "https://registry.npmjs.org/minimatch/-/minimatch-10.2.5.tgz" - integrity sha512-MULkVLfKGYDFYejP07QOurDLLQpcjk7Fw+7jXS2R2czRQzR56yHRveU5NDJEOviH+hETZKSkIk5c+T23GjFUMg== - dependencies: - brace-expansion "^5.0.5" - -minimatch@^3.1.2, minimatch@^3.1.5: - version "3.1.5" - resolved "https://registry.npmjs.org/minimatch/-/minimatch-3.1.5.tgz" - integrity sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w== - dependencies: - brace-expansion "^1.1.7" - -minimist@^1.2.0, minimist@^1.2.6: - version "1.2.8" - resolved "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz" - integrity sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA== - -ms@^2.1.1, ms@^2.1.3: - version "2.1.3" - resolved "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz" - integrity sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA== - -n8ao@^2.0.0: - version "2.0.0" - resolved "https://registry.npmjs.org/n8ao/-/n8ao-2.0.0.tgz" - integrity sha512-7oajUGXk10jJIcjGxOgjRY2X/gy5wiLOY4eOiAfFJ51ljN6Djmsg+j8HMOip+SpqV7OkwzF9VCkDRLluxVlySA== - -nanoid@^3.3.16: - version "3.3.16" - resolved "https://registry.npmjs.org/nanoid/-/nanoid-3.3.16.tgz" - integrity sha512-bzlKTyNJ7+LdGIIwy8ijFpIqEQIvafahV7eYykJ8Cvh42EdJeODoJ6gUJXpQJvej1BddH8OqTXZNE/KfbWAu8Q== - -natural-compare@^1.4.0: - version "1.4.0" - resolved "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz" - integrity sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw== - -node-exports-info@^1.6.0: - version "1.6.2" - resolved "https://registry.npmjs.org/node-exports-info/-/node-exports-info-1.6.2.tgz" - integrity sha512-kXs9Go0cah0qHVV2v389IXQLdLCeE1xfFtjOAF+iobu0OIoG1pje8At2vMHyaPMiPMnG/LWP50twML21eMcAag== - dependencies: - array.prototype.flatmap "^1.3.3" - es-errors "^1.3.0" - object.entries "^1.1.9" - semver "^6.3.1" - -node-releases@^2.0.51: - version "2.0.51" - resolved "https://registry.npmjs.org/node-releases/-/node-releases-2.0.51.tgz" - integrity sha512-wRNIrw4DmVLKQlbgOMdkMx27Wrpzes2hh5Jtbi2bjPd+4wJstWIqP5A+lscnqbm0xxmT5Bpg8Lec5ItEBwx6BQ== - -object-assign@^4.1.1: - version "4.1.1" - resolved "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz" - integrity sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg== - -object-inspect@^1.13.3, object-inspect@^1.13.4: - version "1.13.4" - resolved "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz" - integrity sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew== - -object-keys@^1.1.1: - version "1.1.1" - resolved "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz" - integrity sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA== - -object.assign@^4.1.4, object.assign@^4.1.7: - version "4.1.7" - resolved "https://registry.npmjs.org/object.assign/-/object.assign-4.1.7.tgz" - integrity sha512-nK28WOo+QIjBkDduTINE4JkF/UJJKyf2EJxvJKfblDpyg0Q+pkOHNTL0Qwy6NP6FhE/EnzV73BxxqcJaXY9anw== - dependencies: - call-bind "^1.0.8" - call-bound "^1.0.3" - define-properties "^1.2.1" - es-object-atoms "^1.0.0" - has-symbols "^1.1.0" - object-keys "^1.1.1" - -object.entries@^1.1.9: - version "1.1.9" - resolved "https://registry.npmjs.org/object.entries/-/object.entries-1.1.9.tgz" - integrity sha512-8u/hfXFRBD1O0hPUjioLhoWFHRmt6tKA4/vZPyckBr18l1KE9uHrFaFaUi8MDRTpi4uak2goyPTSNJLXX2k2Hw== - dependencies: - call-bind "^1.0.8" - call-bound "^1.0.4" - define-properties "^1.2.1" - es-object-atoms "^1.1.1" - -object.fromentries@^2.0.8: - version "2.0.8" - resolved "https://registry.npmjs.org/object.fromentries/-/object.fromentries-2.0.8.tgz" - integrity sha512-k6E21FzySsSK5a21KRADBd/NGneRegFO5pLHfdQLpRDETUNJueLXs3WCzyQ3tFRDYgbq3KHGXfTbi2bs8WQ6rQ== - dependencies: - call-bind "^1.0.7" - define-properties "^1.2.1" - es-abstract "^1.23.2" - es-object-atoms "^1.0.0" - -object.groupby@^1.0.3: - version "1.0.3" - resolved "https://registry.npmjs.org/object.groupby/-/object.groupby-1.0.3.tgz" - integrity sha512-+Lhy3TQTuzXI5hevh8sBGqbmurHbbIjAi0Z4S63nthVLmLxfbj4T54a4CfZrXIrt9iP4mVAPYMo/v99taj3wjQ== - dependencies: - call-bind "^1.0.7" - define-properties "^1.2.1" - es-abstract "^1.23.2" - -object.values@^1.1.6, object.values@^1.2.1: - version "1.2.1" - resolved "https://registry.npmjs.org/object.values/-/object.values-1.2.1.tgz" - integrity sha512-gXah6aZrcUxjWg2zR2MwouP2eHlCBzdV4pygudehaKXSGW4v2AsRQUK+lwwXhii6KFZcunEnmSUoYp5CXibxtA== - dependencies: - call-bind "^1.0.8" - call-bound "^1.0.3" - define-properties "^1.2.1" - es-object-atoms "^1.0.0" - -obug@^2.1.1: - version "2.1.4" - resolved "https://registry.npmjs.org/obug/-/obug-2.1.4.tgz" - integrity sha512-4a+OsYv9UktOJKE+l1A4OufDgdRF9PifWj+tJnHURo/P+WOxpG4GzUFL9qCalmWauao6ogiG+QvnCovwPoyAWA== - -optionator@^0.9.3: - version "0.9.4" - resolved "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz" - integrity sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g== - dependencies: - deep-is "^0.1.3" - fast-levenshtein "^2.0.6" - levn "^0.4.1" - prelude-ls "^1.2.1" - type-check "^0.4.0" - word-wrap "^1.2.5" - -own-keys@^1.0.1: - version "1.0.2" - resolved "https://registry.npmjs.org/own-keys/-/own-keys-1.0.2.tgz" - integrity sha512-19YVAg7T+WTrxggPukVq7DjTv6+PJ867TmhCvBsYwmbFCsZd344rq2Ld1p0wo8f8Qrrhgp82c6FJRqdXWtSEhg== - dependencies: - call-bound "^1.0.4" - get-intrinsic "^1.3.0" - object-keys "^1.1.1" - safe-push-apply "^1.0.0" - -p-limit@^3.0.2: - version "3.1.0" - resolved "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz" - integrity sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ== - dependencies: - yocto-queue "^0.1.0" - -p-locate@^5.0.0: - version "5.0.0" - resolved "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz" - integrity sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw== - dependencies: - p-limit "^3.0.2" - -parent-module@^1.0.0: - version "1.0.1" - resolved "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz" - integrity sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g== - dependencies: - callsites "^3.0.0" - -path-exists@^4.0.0: - version "4.0.0" - resolved "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz" - integrity sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w== - -path-key@^3.1.0: - version "3.1.1" - resolved "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz" - integrity sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q== - -path-parse@^1.0.7: - version "1.0.7" - resolved "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz" - integrity sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw== - -pathe@^2.0.3: - version "2.0.3" - resolved "https://registry.npmjs.org/pathe/-/pathe-2.0.3.tgz" - integrity sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w== - -picocolors@^1.1.1: - version "1.1.1" - resolved "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz" - integrity sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA== - -picomatch@^4.0.3, picomatch@^4.0.4, picomatch@^4.0.5: - version "4.0.5" - resolved "https://registry.npmjs.org/picomatch/-/picomatch-4.0.5.tgz" - integrity sha512-RvwwcruNjI1ncT5xRakeyS9Lf8lcItv34KD+aif+VH9kduAyfYBipGh12274xtenIPZ119/R9BdTBa8gAwSh0A== - -possible-typed-array-names@^1.0.0, possible-typed-array-names@^1.1.0: - version "1.1.0" - resolved "https://registry.npmjs.org/possible-typed-array-names/-/possible-typed-array-names-1.1.0.tgz" - integrity sha512-/+5VFTchJDoVj3bhoqi6UeymcD00DAwb1nJwamzPvHEszJ4FpF6SNNbUbOS8yI56qHzdV8eK0qEfOSiodkTdxg== - -postcss@^8.5.17: - version "8.5.22" - resolved "https://registry.npmjs.org/postcss/-/postcss-8.5.22.tgz" - integrity sha512-KBDEIpLrvpv16pp3K0Fw+UCoZfopFjjgeB+0tA/aaThfEE74kKDLrgg603YvOWJyg3+WYtyq3xYsQWsIyZlPqQ== - dependencies: - nanoid "^3.3.16" - picocolors "^1.1.1" - source-map-js "^1.2.1" - -postprocessing@^6.39.3: - version "6.39.3" - resolved "https://registry.npmjs.org/postprocessing/-/postprocessing-6.39.3.tgz" - integrity sha512-h5H1iuN96aRkU06CzJ8d/FqFe3Qs2bI7LiGHTzOI5T5mQqjzovi2t1EkRHb8qkHgoD9cTJDb4uA30AkSxsFB7A== - -prelude-ls@^1.2.1: - version "1.2.1" - resolved "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz" - integrity sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g== - -prettier-linter-helpers@^1.0.1: - version "1.0.1" - resolved "https://registry.npmjs.org/prettier-linter-helpers/-/prettier-linter-helpers-1.0.1.tgz" - integrity sha512-SxToR7P8Y2lWmv/kTzVLC1t/GDI2WGjMwNhLLE9qtH8Q13C+aEmuRlzDst4Up4s0Wc8sF2M+J57iB3cMLqftfg== - dependencies: - fast-diff "^1.1.2" - -prettier@^3.9.5: - version "3.9.6" - resolved "https://registry.npmjs.org/prettier/-/prettier-3.9.6.tgz" - integrity sha512-OpN0zzVdiaiAhxpuuj5efpIS4sY9j7bY6uR5mnj5yPzGkdkjNKSJeUThPb60Jw29QuAZgA4o+/iB49kFiaBX6g== - -prop-types@^15.8.1: - version "15.8.1" - resolved "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz" - integrity sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg== - dependencies: - loose-envify "^1.4.0" - object-assign "^4.1.1" - react-is "^16.13.1" - -punycode@^2.1.0: - version "2.3.1" - resolved "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz" - integrity sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg== - -react-is@^16.13.1: - version "16.13.1" - resolved "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz" - integrity sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ== - -react-use-measure@^2.1.7: - version "2.1.7" - resolved "https://registry.npmjs.org/react-use-measure/-/react-use-measure-2.1.7.tgz" - integrity sha512-KrvcAo13I/60HpwGO5jpW7E9DfusKyLPLvuHlUyP5zqnmAPhNc6qTRjUQrdTADl0lpPpDVU2/Gg51UlOGHXbdg== - -react@^19.2.7: - version "19.2.8" - resolved "https://registry.npmjs.org/react/-/react-19.2.8.tgz" - integrity sha512-PWaYA1L/q9u2u7xYQi+Y3L3Yfnie7XyLeaJICV1MGD6LprsBxcAqGjYyr0eY3p+QdsA+x/Irkt4Qif8D63+Sbw== - -reflect.getprototypeof@^1.0.10, reflect.getprototypeof@^1.0.9: - version "1.0.10" - resolved "https://registry.npmjs.org/reflect.getprototypeof/-/reflect.getprototypeof-1.0.10.tgz" - integrity sha512-00o4I+DVrefhv+nX0ulyi3biSHCPDe+yLv5o/p6d/UVlirijB8E16FtfwSAi4g3tcqrQ4lRAqQSoFEZJehYEcw== - dependencies: - call-bind "^1.0.8" - define-properties "^1.2.1" - es-abstract "^1.23.9" - es-errors "^1.3.0" - es-object-atoms "^1.0.0" - get-intrinsic "^1.2.7" - get-proto "^1.0.1" - which-builtin-type "^1.2.1" - -regexp.prototype.flags@^1.5.3, regexp.prototype.flags@^1.5.4: - version "1.5.4" - resolved "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.5.4.tgz" - integrity sha512-dYqgNSZbDwkaJ2ceRd9ojCGjBq+mOm9LmtXnAnEGyHhN/5R7iDW2TRw3h+o/jCFxus3P2LfWIIiwowAjANm7IA== - dependencies: - call-bind "^1.0.8" - define-properties "^1.2.1" - es-errors "^1.3.0" - get-proto "^1.0.1" - gopd "^1.2.0" - set-function-name "^2.0.2" - -resolve-from@^4.0.0: - version "4.0.0" - resolved "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz" - integrity sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g== - -resolve@^2.0.0-next.5, resolve@^2.0.0-next.6: - version "2.0.0-next.7" - resolved "https://registry.npmjs.org/resolve/-/resolve-2.0.0-next.7.tgz" - integrity sha512-tqt+NBWwyaMgw3zDsnygx4CByWjQEJHOPMdslYhppaQSJUtL/D4JO9CcBBlhPoI8lz9oJIDXkwXfhF4aWqP8xQ== - dependencies: - es-errors "^1.3.0" - is-core-module "^2.16.2" - node-exports-info "^1.6.0" - object-keys "^1.1.1" - path-parse "^1.0.7" - supports-preserve-symlinks-flag "^1.0.0" - -rolldown@~1.1.5: - version "1.1.5" - resolved "https://registry.npmjs.org/rolldown/-/rolldown-1.1.5.tgz" - integrity sha512-t9z29cJjXf/vxQ8dyhCSpt6H6aSwHTk8cT5I3iy6SMXuFpk5mB6PL6XfC8PCwrPTx93udwKUm9HRteAlTGBLiA== - dependencies: - "@oxc-project/types" "=0.139.0" - "@rolldown/pluginutils" "^1.0.0" - optionalDependencies: - "@rolldown/binding-android-arm64" "1.1.5" - "@rolldown/binding-darwin-arm64" "1.1.5" - "@rolldown/binding-darwin-x64" "1.1.5" - "@rolldown/binding-freebsd-x64" "1.1.5" - "@rolldown/binding-linux-arm-gnueabihf" "1.1.5" - "@rolldown/binding-linux-arm64-gnu" "1.1.5" - "@rolldown/binding-linux-arm64-musl" "1.1.5" - "@rolldown/binding-linux-ppc64-gnu" "1.1.5" - "@rolldown/binding-linux-s390x-gnu" "1.1.5" - "@rolldown/binding-linux-x64-gnu" "1.1.5" - "@rolldown/binding-linux-x64-musl" "1.1.5" - "@rolldown/binding-openharmony-arm64" "1.1.5" - "@rolldown/binding-wasm32-wasi" "1.1.5" - "@rolldown/binding-win32-arm64-msvc" "1.1.5" - "@rolldown/binding-win32-x64-msvc" "1.1.5" - -safe-array-concat@^1.1.3: - version "1.1.4" - resolved "https://registry.npmjs.org/safe-array-concat/-/safe-array-concat-1.1.4.tgz" - integrity sha512-wtZlHyOje6OZTGqAoaDKxFkgRtkF9CnHAVnCHKfuj200wAgL+bSJhdsCD2l0Qx/2ekEXjPWcyKkfGb5CPboslg== - dependencies: - call-bind "^1.0.9" - call-bound "^1.0.4" - get-intrinsic "^1.3.0" - has-symbols "^1.1.0" - isarray "^2.0.5" - -safe-push-apply@^1.0.0: - version "1.0.0" - resolved "https://registry.npmjs.org/safe-push-apply/-/safe-push-apply-1.0.0.tgz" - integrity sha512-iKE9w/Z7xCzUMIZqdBsp6pEQvwuEebH4vdpjcDWnyzaI6yl6O9FHvVpmGelvEHNsoY6wGblkxR6Zty/h00WiSA== - dependencies: - es-errors "^1.3.0" - isarray "^2.0.5" - -safe-regex-test@^1.1.0: - version "1.1.0" - resolved "https://registry.npmjs.org/safe-regex-test/-/safe-regex-test-1.1.0.tgz" - integrity sha512-x/+Cz4YrimQxQccJf5mKEbIa1NzeCRNI5Ecl/ekmlYaampdNLPalVyIcCZNNH3MvmqBugV5TMYZXv0ljslUlaw== - dependencies: - call-bound "^1.0.2" - es-errors "^1.3.0" - is-regex "^1.2.1" - -scheduler@^0.27.0: - version "0.27.0" - resolved "https://registry.npmjs.org/scheduler/-/scheduler-0.27.0.tgz" - integrity sha512-eNv+WrVbKu1f3vbYJT/xtiF5syA5HPIMtf9IgY/nKg0sWqzAUEvqY/xm7OcZc/qafLx/iO9FgOmeSAp4v5ti/Q== - -semver@^6.3.1: - version "6.3.1" - resolved "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz" - integrity sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA== - -semver@^7.7.3: - version "7.8.5" - resolved "https://registry.npmjs.org/semver/-/semver-7.8.5.tgz" - integrity sha512-Y7/KDsb8LjooZpwaqGyulO6DQlksgCncchHGk+sZIY4SBvUocMBEFH5Ur1fI4dV+Jvl0w6cjvucaIi40puRioA== - -set-function-length@^1.2.2: - version "1.2.2" - resolved "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.2.tgz" - integrity sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg== - dependencies: - define-data-property "^1.1.4" - es-errors "^1.3.0" - function-bind "^1.1.2" - get-intrinsic "^1.2.4" - gopd "^1.0.1" - has-property-descriptors "^1.0.2" - -set-function-name@^2.0.2: - version "2.0.2" - resolved "https://registry.npmjs.org/set-function-name/-/set-function-name-2.0.2.tgz" - integrity sha512-7PGFlmtwsEADb0WYyvCMa1t+yke6daIG4Wirafur5kcf+MhUnPms1UeR0CKQdTZD81yESwMHbtn+TR+dMviakQ== - dependencies: - define-data-property "^1.1.4" - es-errors "^1.3.0" - functions-have-names "^1.2.3" - has-property-descriptors "^1.0.2" - -set-proto@^1.0.0: - version "1.0.0" - resolved "https://registry.npmjs.org/set-proto/-/set-proto-1.0.0.tgz" - integrity sha512-RJRdvCo6IAnPdsvP/7m6bsQqNnn1FCBX5ZNtFL98MmFF/4xAIJTIg1YbHW5DC2W5SKZanrC6i4HsJqlajw/dZw== - dependencies: - dunder-proto "^1.0.1" - es-errors "^1.3.0" - es-object-atoms "^1.0.0" - -shebang-command@^2.0.0: - version "2.0.0" - resolved "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz" - integrity sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA== - dependencies: - shebang-regex "^3.0.0" - -shebang-regex@^3.0.0: - version "3.0.0" - resolved "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz" - integrity sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A== - -side-channel-list@^1.0.1: - version "1.0.1" - resolved "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.1.tgz" - integrity sha512-mjn/0bi/oUURjc5Xl7IaWi/OJJJumuoJFQJfDDyO46+hBWsfaVM65TBHq2eoZBhzl9EchxOijpkbRC8SVBQU0w== - dependencies: - es-errors "^1.3.0" - object-inspect "^1.13.4" - -side-channel-map@^1.0.1: - version "1.0.1" - resolved "https://registry.npmjs.org/side-channel-map/-/side-channel-map-1.0.1.tgz" - integrity sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA== - dependencies: - call-bound "^1.0.2" - es-errors "^1.3.0" - get-intrinsic "^1.2.5" - object-inspect "^1.13.3" - -side-channel-weakmap@^1.0.2: - version "1.0.2" - resolved "https://registry.npmjs.org/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz" - integrity sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A== - dependencies: - call-bound "^1.0.2" - es-errors "^1.3.0" - get-intrinsic "^1.2.5" - object-inspect "^1.13.3" - side-channel-map "^1.0.1" - -side-channel@^1.1.0: - version "1.1.1" - resolved "https://registry.npmjs.org/side-channel/-/side-channel-1.1.1.tgz" - integrity sha512-6x6dK6zJdpTzF4sQeNYxwtvBzf6Eg4GtlesS94HOvTudUeyK2WXAaIfmDgsyslYrRBeFIlsi54AYsFGUuhmvrQ== - dependencies: - es-errors "^1.3.0" - object-inspect "^1.13.4" - side-channel-list "^1.0.1" - side-channel-map "^1.0.1" - side-channel-weakmap "^1.0.2" - -siginfo@^2.0.0: - version "2.0.0" - resolved "https://registry.npmjs.org/siginfo/-/siginfo-2.0.0.tgz" - integrity sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g== - -source-map-js@^1.2.1: - version "1.2.1" - resolved "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz" - integrity sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA== - -stackback@0.0.2: - version "0.0.2" - resolved "https://registry.npmjs.org/stackback/-/stackback-0.0.2.tgz" - integrity sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw== - -std-env@^4.0.0-rc.1: - version "4.2.0" - resolved "https://registry.npmjs.org/std-env/-/std-env-4.2.0.tgz" - integrity sha512-oCUKSupKTHX53EyjDtuZQ64pjLJ6yYCtpmEw0goYxtjG9KpbRe8KAsl2tBUGU9DyMcJ0RwJ8GqJAFzMXcXW1Rw== - -stop-iteration-iterator@^1.1.0: - version "1.1.0" - resolved "https://registry.npmjs.org/stop-iteration-iterator/-/stop-iteration-iterator-1.1.0.tgz" - integrity sha512-eLoXW/DHyl62zxY4SCaIgnRhuMr6ri4juEYARS8E6sCEqzKpOiE521Ucofdx+KnDZl5xmvGYaaKCk5FEOxJCoQ== - dependencies: - es-errors "^1.3.0" - internal-slot "^1.1.0" - -string.prototype.matchall@^4.0.12: - version "4.0.12" - resolved "https://registry.npmjs.org/string.prototype.matchall/-/string.prototype.matchall-4.0.12.tgz" - integrity sha512-6CC9uyBL+/48dYizRf7H7VAYCMCNTBeM78x/VTUe9bFEaxBepPJDa1Ow99LqI/1yF7kuy7Q3cQsYMrcjGUcskA== - dependencies: - call-bind "^1.0.8" - call-bound "^1.0.3" - define-properties "^1.2.1" - es-abstract "^1.23.6" - es-errors "^1.3.0" - es-object-atoms "^1.0.0" - get-intrinsic "^1.2.6" - gopd "^1.2.0" - has-symbols "^1.1.0" - internal-slot "^1.1.0" - regexp.prototype.flags "^1.5.3" - set-function-name "^2.0.2" - side-channel "^1.1.0" - -string.prototype.repeat@^1.0.0: - version "1.0.0" - resolved "https://registry.npmjs.org/string.prototype.repeat/-/string.prototype.repeat-1.0.0.tgz" - integrity sha512-0u/TldDbKD8bFCQ/4f5+mNRrXwZ8hg2w7ZR8wa16e8z9XpePWl3eGEcUD0OXpEH/VJH/2G3gjUtR3ZOiBe2S/w== - dependencies: - define-properties "^1.1.3" - es-abstract "^1.17.5" - -string.prototype.trim@^1.2.10: - version "1.2.11" - resolved "https://registry.npmjs.org/string.prototype.trim/-/string.prototype.trim-1.2.11.tgz" - integrity sha512-PwvK7BU+CMTJGYQCTZb5RWXIML92lftJLhQz1tBzgKiqGxJaMlBAa48POXaNAC2s4y8jr3EFqrkF9+44neS46w== - dependencies: - call-bind "^1.0.9" - call-bound "^1.0.4" - define-data-property "^1.1.4" - define-properties "^1.2.1" - es-abstract "^1.24.2" - es-object-atoms "^1.1.2" - has-property-descriptors "^1.0.2" - safe-regex-test "^1.1.0" - -string.prototype.trimend@^1.0.9: - version "1.0.10" - resolved "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.10.tgz" - integrity sha512-2+3aDAOmPTmuFwjDnmJG2ctEkQKVki7vOSqaxkv42Mowj1V6PnvuwFCRrR5lChUux1TBskPjfkeTOhqczDMxTw== - dependencies: - call-bind "^1.0.9" - call-bound "^1.0.4" - define-properties "^1.2.1" - es-object-atoms "^1.1.2" - -string.prototype.trimstart@^1.0.8: - version "1.0.8" - resolved "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.8.tgz" - integrity sha512-UXSH262CSZY1tfu3G3Secr6uGLCFVPMhIqHjlgCUtCCcgihYc/xKs9djMTMUOb2j1mVSeU8EU6NWc/iQKU6Gfg== - dependencies: - call-bind "^1.0.7" - define-properties "^1.2.1" - es-object-atoms "^1.0.0" - -strip-bom@^3.0.0: - version "3.0.0" - resolved "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz" - integrity sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA== - -strip-json-comments@^3.1.1: - version "3.1.1" - resolved "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz" - integrity sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig== - -supports-color@^7.1.0: - version "7.2.0" - resolved "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz" - integrity sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw== - dependencies: - has-flag "^4.0.0" - -supports-preserve-symlinks-flag@^1.0.0: - version "1.0.0" - resolved "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz" - integrity sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w== - -suspend-react@^0.1.3: - version "0.1.3" - resolved "https://registry.npmjs.org/suspend-react/-/suspend-react-0.1.3.tgz" - integrity sha512-aqldKgX9aZqpoDp3e8/BZ8Dm7x1pJl+qI3ZKxDN0i/IQTWUwBx/ManmlVJ3wowqbno6c2bmiIfs+Um6LbsjJyQ== - -synckit@^0.11.13: - version "0.11.13" - resolved "https://registry.npmjs.org/synckit/-/synckit-0.11.13.tgz" - integrity sha512-eNRKgb3z66Yp3D2CixVujOUvXLFUTij/zVnV8KRyvFdQwpz7I5DS8UfRkTeLzb64u+dkzDSdelE24izu+zSSUg== - dependencies: - "@pkgr/core" "^0.3.6" - -three@^0.182.0: - version "0.182.0" - resolved "https://registry.npmjs.org/three/-/three-0.182.0.tgz" - integrity sha512-GbHabT+Irv+ihI1/f5kIIsZ+Ef9Sl5A1Y7imvS5RQjWgtTPfPnZ43JmlYI7NtCRDK9zir20lQpfg8/9Yd02OvQ== - -tinybench@^2.9.0: - version "2.9.0" - resolved "https://registry.npmjs.org/tinybench/-/tinybench-2.9.0.tgz" - integrity sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg== - -tinyexec@^1.0.2: - version "1.2.4" - resolved "https://registry.npmjs.org/tinyexec/-/tinyexec-1.2.4.tgz" - integrity sha512-SHf/r48b7vOrjve9PxJo3MN5v5yuyjHvdUcrQffT3WXMUfnGmHDVbC4k3sHJaJTgZCwpUplIaAo5ANtMyp3YHg== - -tinyglobby@^0.2.15, tinyglobby@^0.2.17: - version "0.2.17" - resolved "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.17.tgz" - integrity sha512-wXR/dYpcqKmfWpEdZjiKJOwCNFndD0DMnrW/cYjVGttEkBfVgcLFHoNrlj47mjOVic9yyNu65alsgF4NQyTa2g== - dependencies: - fdir "^6.5.0" - picomatch "^4.0.4" - -tinyrainbow@^3.1.0: - version "3.1.0" - resolved "https://registry.npmjs.org/tinyrainbow/-/tinyrainbow-3.1.0.tgz" - integrity sha512-Bf+ILmBgretUrdJxzXM0SgXLZ3XfiaUuOj/IKQHuTXip+05Xn+uyEYdVg0kYDipTBcLrCVyUzAPz7QmArb0mmw== - -ts-api-utils@^2.5.0: - version "2.5.0" - resolved "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-2.5.0.tgz" - integrity sha512-OJ/ibxhPlqrMM0UiNHJ/0CKQkoKF243/AEmplt3qpRgkW8VG7IfOS41h7V8TjITqdByHzrjcS/2si+y4lIh8NA== - -tsconfig-paths@^3.15.0: - version "3.15.0" - resolved "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.15.0.tgz" - integrity sha512-2Ac2RgzDe/cn48GvOe3M+o82pEFewD3UPbyoUHHdKasHwJKjds4fLXWf/Ux5kATBKN20oaFGu+jbElp1pos0mg== - dependencies: - "@types/json5" "^0.0.29" - json5 "^1.0.2" - minimist "^1.2.6" - strip-bom "^3.0.0" - -tslib@^2.4.0: - version "2.8.1" - resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.8.1.tgz#612efe4ed235d567e8aba5f2a5fab70280ade83f" - integrity sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w== - -type-check@^0.4.0, type-check@~0.4.0: - version "0.4.0" - resolved "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz" - integrity sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew== - dependencies: - prelude-ls "^1.2.1" - -typed-array-buffer@^1.0.3: - version "1.0.3" - resolved "https://registry.npmjs.org/typed-array-buffer/-/typed-array-buffer-1.0.3.tgz" - integrity sha512-nAYYwfY3qnzX30IkA6AQZjVbtK6duGontcQm1WSG1MD94YLqK0515GNApXkoxKOWMusVssAHWLh9SeaoefYFGw== - dependencies: - call-bound "^1.0.3" - es-errors "^1.3.0" - is-typed-array "^1.1.14" - -typed-array-byte-length@^1.0.3: - version "1.0.3" - resolved "https://registry.npmjs.org/typed-array-byte-length/-/typed-array-byte-length-1.0.3.tgz" - integrity sha512-BaXgOuIxz8n8pIq3e7Atg/7s+DpiYrxn4vdot3w9KbnBhcRQq6o3xemQdIfynqSeXeDrF32x+WvfzmOjPiY9lg== - dependencies: - call-bind "^1.0.8" - for-each "^0.3.3" - gopd "^1.2.0" - has-proto "^1.2.0" - is-typed-array "^1.1.14" - -typed-array-byte-offset@^1.0.4: - version "1.0.4" - resolved "https://registry.npmjs.org/typed-array-byte-offset/-/typed-array-byte-offset-1.0.4.tgz" - integrity sha512-bTlAFB/FBYMcuX81gbL4OcpH5PmlFHqlCCpAl8AlEzMz5k53oNDvN8p1PNOWLEmI2x4orp3raOFB51tv9X+MFQ== - dependencies: - available-typed-arrays "^1.0.7" - call-bind "^1.0.8" - for-each "^0.3.3" - gopd "^1.2.0" - has-proto "^1.2.0" - is-typed-array "^1.1.15" - reflect.getprototypeof "^1.0.9" - -typed-array-length@^1.0.7: - version "1.0.8" - resolved "https://registry.npmjs.org/typed-array-length/-/typed-array-length-1.0.8.tgz" - integrity sha512-phPGCwqr2+Qo0fwniCE8e4pKnGu/yFb5nD5Y8bf0EEeiI5GklnACYA9GFy/DrAeRrKHXvHn+1SUsOWgJp6RO+g== - dependencies: - call-bind "^1.0.9" - for-each "^0.3.5" - gopd "^1.2.0" - is-typed-array "^1.1.15" - possible-typed-array-names "^1.1.0" - reflect.getprototypeof "^1.0.10" - -typescript@^6.0.0: - version "6.0.3" - resolved "https://registry.npmjs.org/typescript/-/typescript-6.0.3.tgz" - integrity sha512-y2TvuxSZPDyQakkFRPZHKFm+KKVqIisdg9/CZwm9ftvKXLP8NRWj38/ODjNbr43SsoXqNuAisEf1GdCxqWcdBw== - -unbox-primitive@^1.1.0: - version "1.1.0" - resolved "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.1.0.tgz" - integrity sha512-nWJ91DjeOkej/TA8pXQ3myruKpKEYgqvpw9lz4OPHj/NWFNluYrjbz9j01CJ8yKQd2g4jFoOkINCTW2I5LEEyw== - dependencies: - call-bound "^1.0.3" - has-bigints "^1.0.2" - has-symbols "^1.1.0" - which-boxed-primitive "^1.1.1" - -undici-types@~8.3.0: - version "8.3.0" - resolved "https://registry.npmjs.org/undici-types/-/undici-types-8.3.0.tgz" - integrity sha512-j375ScV60dom+YkPFIfTLcOiPxkN/buHz5GobjLhixFuANaNs3C9l4GmrWqejgXWJ7BbJcFYpTEUkS1Ge8bpZQ== - -update-browserslist-db@^1.2.3: - version "1.2.3" - resolved "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.2.3.tgz" - integrity sha512-Js0m9cx+qOgDxo0eMiFGEueWztz+d4+M3rGlmKPT+T4IS/jP4ylw3Nwpu6cpTTP8R1MAC1kF4VbdLt3ARf209w== - dependencies: - escalade "^3.2.0" - picocolors "^1.1.1" - -uri-js@^4.2.2: - version "4.4.1" - resolved "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz" - integrity sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg== - dependencies: - punycode "^2.1.0" - -use-sync-external-store@^1.4.0: - version "1.6.0" - resolved "https://registry.npmjs.org/use-sync-external-store/-/use-sync-external-store-1.6.0.tgz" - integrity sha512-Pp6GSwGP/NrPIrxVFAIkOQeyw8lFenOHijQWkUTrDvrF4ALqylP2C/KCkeS9dpUM3KvYRQhna5vt7IL95+ZQ9w== - -"vite@^6.0.0 || ^7.0.0 || ^8.0.0", vite@^8.1.4: - version "8.1.5" - resolved "https://registry.npmjs.org/vite/-/vite-8.1.5.tgz" - integrity sha512-7ULLwsCdYx/nRyrpiEwvqb5TFHrMVZyBt+rg/OAXT7rgj/z+DtTDyKFeLAdDkubDVDKD8jOsndmy7m55XcfUsw== - dependencies: - lightningcss "^1.32.0" - picomatch "^4.0.5" - postcss "^8.5.17" - rolldown "~1.1.5" - tinyglobby "^0.2.17" - optionalDependencies: - fsevents "~2.3.3" - -vitest@^4.1.10: - version "4.1.10" - resolved "https://registry.npmjs.org/vitest/-/vitest-4.1.10.tgz" - integrity sha512-R9jUTe5S4Qb0HCd4TNqpC7oGcrMssMRGXLW80ubjWsW9VH5GF8y1Y0SFLY9AbqSk6nt0PnOx4H4WNJYZ13GUPw== - dependencies: - "@vitest/expect" "4.1.10" - "@vitest/mocker" "4.1.10" - "@vitest/pretty-format" "4.1.10" - "@vitest/runner" "4.1.10" - "@vitest/snapshot" "4.1.10" - "@vitest/spy" "4.1.10" - "@vitest/utils" "4.1.10" - es-module-lexer "^2.0.0" - expect-type "^1.3.0" - magic-string "^0.30.21" - obug "^2.1.1" - pathe "^2.0.3" - picomatch "^4.0.3" - std-env "^4.0.0-rc.1" - tinybench "^2.9.0" - tinyexec "^1.0.2" - tinyglobby "^0.2.15" - tinyrainbow "^3.1.0" - vite "^6.0.0 || ^7.0.0 || ^8.0.0" - why-is-node-running "^2.3.0" - -which-boxed-primitive@^1.1.0, which-boxed-primitive@^1.1.1: - version "1.1.1" - resolved "https://registry.npmjs.org/which-boxed-primitive/-/which-boxed-primitive-1.1.1.tgz" - integrity sha512-TbX3mj8n0odCBFVlY8AxkqcHASw3L60jIuF8jFP78az3C2YhmGvqbHBpAjTRH2/xqYunrJ9g1jSyjCjpoWzIAA== - dependencies: - is-bigint "^1.1.0" - is-boolean-object "^1.2.1" - is-number-object "^1.1.1" - is-string "^1.1.1" - is-symbol "^1.1.1" - -which-builtin-type@^1.2.1: - version "1.2.1" - resolved "https://registry.npmjs.org/which-builtin-type/-/which-builtin-type-1.2.1.tgz" - integrity sha512-6iBczoX+kDQ7a3+YJBnh3T+KZRxM/iYNPXicqk66/Qfm1b93iu+yOImkg0zHbj5LNOcNv1TEADiZ0xa34B4q6Q== - dependencies: - call-bound "^1.0.2" - function.prototype.name "^1.1.6" - has-tostringtag "^1.0.2" - is-async-function "^2.0.0" - is-date-object "^1.1.0" - is-finalizationregistry "^1.1.0" - is-generator-function "^1.0.10" - is-regex "^1.2.1" - is-weakref "^1.0.2" - isarray "^2.0.5" - which-boxed-primitive "^1.1.0" - which-collection "^1.0.2" - which-typed-array "^1.1.16" - -which-collection@^1.0.2: - version "1.0.2" - resolved "https://registry.npmjs.org/which-collection/-/which-collection-1.0.2.tgz" - integrity sha512-K4jVyjnBdgvc86Y6BkaLZEN933SwYOuBFkdmBu9ZfkcAbdVbpITnDmjvZ/aQjRXQrv5EPkTnD1s39GiiqbngCw== - dependencies: - is-map "^2.0.3" - is-set "^2.0.3" - is-weakmap "^2.0.2" - is-weakset "^2.0.3" - -which-typed-array@^1.1.16, which-typed-array@^1.1.19: - version "1.1.22" - resolved "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.22.tgz" - integrity sha512-fvO4ExWMFsqyhG3AiPAObMuY1lxaqgYcxbc49CNdWDDECOJNgQyvsOWVwbZc+qf3rzRtxojBK+CMEv0Ld5CYpw== - dependencies: - available-typed-arrays "^1.0.7" - call-bind "^1.0.9" - call-bound "^1.0.4" - for-each "^0.3.5" - get-proto "^1.0.1" - gopd "^1.2.0" - has-tostringtag "^1.0.2" - -which@^2.0.1: - version "2.0.2" - resolved "https://registry.npmjs.org/which/-/which-2.0.2.tgz" - integrity sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA== - dependencies: - isexe "^2.0.0" - -why-is-node-running@^2.3.0: - version "2.3.0" - resolved "https://registry.npmjs.org/why-is-node-running/-/why-is-node-running-2.3.0.tgz" - integrity sha512-hUrmaWBdVDcxvYqnyh09zunKzROWjbZTiNy8dBEjkS7ehEDQibXJ7XvlmtbwuTclUiIyN+CyXQD4Vmko8fNm8w== - dependencies: - siginfo "^2.0.0" - stackback "0.0.2" - -word-wrap@^1.2.5: - version "1.2.5" - resolved "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz" - integrity sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA== - -yallist@^3.0.2: - version "3.1.1" - resolved "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz" - integrity sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g== - -yocto-queue@^0.1.0: - version "0.1.0" - resolved "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz" - integrity sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q== - -"zod-validation-error@^3.5.0 || ^4.0.0": - version "4.0.2" - resolved "https://registry.npmjs.org/zod-validation-error/-/zod-validation-error-4.0.2.tgz" - integrity sha512-Q6/nZLe6jxuU80qb/4uJ4t5v2VEZ44lzQjPDhYJNztRQ4wyWc6VF3D3Kb/fAuPetZQnhS3hnajCf9CsWesghLQ== - -"zod@^3.25.0 || ^4.0.0": - version "4.4.3" - resolved "https://registry.npmjs.org/zod/-/zod-4.4.3.tgz" - integrity sha512-ytENFjIJFl2UwYglde2jchW2Hwm4GJFLDiSXWdTrJQBIN9Fcyp7n4DhxJEiWNAJMV1/BqWfW/kkg71UDcHJyTQ== - -zustand@^5.0.3: - version "5.0.14" - resolved "https://registry.npmjs.org/zustand/-/zustand-5.0.14.tgz" - integrity sha512-/8tAspM5LMPr28b3fwLYrtdj77ECpfZviaP75CMTnwO8ISyaE4GDIG/9rDDYq/cH9D2Xw2A2RXglLInmVBQB/g==