Repository: pmndrs/zustand
Pull request #1075 (2022-07-10 21:57:55)

feature(persist): add method getOptions on persist api and update documentation
This pr can be relationated to:

- Expose getOptions method on persist api
- Update Documentation with examples of storage event listener
- Add test for the new method
- options were given so that this [issue](https://github.com/pmndrs/zustand/issues/714) can be solved

codesandbox-ci[bot]: This pull request is automatically built and testable in [CodeSandbox](https://codesandbox.io).

  To see build info of the built libraries, click [here](https://ci.codesandbox.io/status/pmndrs/zustand/pr/1075/builds/269687) or the icon next to each commit SHA.

Latest deployment of this branch, based on commit f8729c4279014bec9a52cb86a6f423e43cb1b2fc:

|Sandbox| Source |
|--|--|
|[React](https://codesandbox.io/s/react-7t7cut)| Configuration |
|[React Typescript](https://codesandbox.io/s/react-typescript-5y686h)| Configuration |
|[React Browserify](https://codesandbox.io/s/react-browserify-y5pkv2)| Configuration |
|[React Snowpack](https://codesandbox.io/s/react-snowpack-g9mxbg)| Configuration |
|[React Parcel](https://codesandbox.io/s/react-parcel-4b13kl)| Configuration |
|[Next.js](https://codesandbox.io/s/next-js-xu6kzd)| Configuration |

AnatoleLucet: Hey thanks for the PR
Unfortunately, i don't think this is a good solution to the original issue.

I think the event listener should be directly in the `persist` function/middleware and call `hydrate()` in the event handler.
tbor00: 


> Hey thanks for the PR Unfortunately, i don't think this is a good solution to the original issue.
> 
> I think the event listener should be directly in the `persist` function/middleware and call `hydrate()` in the event handler.

Hi Thanks for your comment, I don't know if I understood you correctly, do you think something like this will work?

<img width="665" alt="Captura de Pantalla 2022-07-10 a la(s) 20 10 07" src="https://user-images.githubusercontent.com/64227164/178165620-9ee34e1c-d5c3-468a-bc62-4ce3390cc1fb.png">


AnatoleLucet: Yes pretty much, although i'm not sure what `options.subscribe.keySubscribe` is. Maybe you meant `options.name`?
Also what's the removeEventListener used for? It feels like we're only reacting to the first storage change and omitting future ones.
tbor00: > Yes pretty much, although i'm not sure what `options.subscribe.keySubscribe` is. Maybe you meant `options.name`? Also what's the removeEventListener used for? It feels like we're only reacting to the first storage change and omitting future ones.

You're right, the changes are already up. I'm waiting for more comments, Thanks
dai-shi: `persist` middleware supports AsyncStorage in ReactNative, and possibly other non-DOM storages. So, you can't make it depend on `window`. Can you please make it an option somehow? (Note that a possible method to check `typeof window` is not recommended because some tools mock it unexpectedly.)
tbor00: > `persist` middleware supports AsyncStorage in ReactNative, and possibly other non-DOM storages. So, you can't make it depend on `window`. Can you please make it an option somehow? (Note that a possible method to check `typeof window` is not recommended because some tools mock it unexpectedly.)

Hmm, You're right. So I'll work on finding the best way to do this.
tbor00: > `persist` middleware supports AsyncStorage in ReactNative, and possibly other non-DOM storages. So, you can't make it depend on `window`. Can you please make it an option somehow? (Note that a possible method to check `typeof window` is not recommended because some tools mock it unexpectedly.)

@dai-shi Do you think adding such a function would be

<img width="1177" alt="Captura de Pantalla 2022-07-10 a la(s) 22 39 11" src="https://user-images.githubusercontent.com/64227164/178172979-e8b8230d-689e-4fd4-acc3-9c91e48afa3c.png">
 worth it?


dai-shi: No, I wouldn't recommend "detecting" anything in the library code. We should just provide a capability (option/method) so that developers can use as they want.
tbor00: > No, no recomendaría "detectar" nada en el código de la biblioteca. Simplemente deberíamos proporcionar una capacidad (opción/método) para que los desarrolladores puedan usarla como quieran.


And a boolean in the options that if it is true execute this block? do you think it could work?
dai-shi: I think I'd like to avoid any DOM related code in `persist` middleware which is just useless for RN users. A separate helper function can be an option.

```js
import { persist } from 'zustand/middleware'
import { someHelperFunctionWithDOM } from 'zustand/middleware/persist'

const useFooStore = create(persist(
  ...,
  {
    someOption: someHelperFunctionWithDOM(...),
  }
))
```

Note that we could have other "subscribe" methods than DOM one. For example, one I can imagine is a storage based on firestore. The point is `persist` middleware is not only for `localStorage`/`sessionStorage`.
tbor00: @dai-shi  Ok, so I think something like what's in the first commit might work
dai-shi: Yeah, something like that. There are still many things to consider, but I think you first want to try creating a working demo?
AnatoleLucet: @dai-shi what about:
```ts
// impl
const withStorageDOMEvent = (store) => {
  window.addEventListener("storage", () => {
    // TODO: add .getOptions method on persist's api
    if (e.key === store.persist.getOptions().name && e.newValue) {
      store.persist.rehydrate()
    }
  })

  return // TODO: removeEventListener
}

// usage
import { withStorageDOMEvent } from "zustand/middleware"

const useMyStore = create(persist(...))

withStorageDOMEvent(useMyStore)
``` 
tbor00: 


> @dai-shi what about:
> 
> ```ts
> // impl
> const withStorageDOMEvent = (store) => {
>   window.addEventListener("storage", () => {
>     // TODO: add .getOptions method on persist's api
>     if (e.key === store.persist.getOptions().name && e.newValue) {
>       store.persist.rehydrate()
>     }
>   })
> 
>   return // TODO: removeEventListener
> }
> 
> // usage
> import { withStorageDOMEvent } from "zustand/middleware"
> 
> const useMyStore = create(persist(...))
> 
> withStorageDOMEvent(useMyStore)
> ```

I think what you propose sounds good too. Here I leave an [example](https://codesandbox.io/s/suscribeeventchangewithdom-example-forked-lql95t?file=/src/App.js:0-1133) of how it could be used with the changes I made
dai-shi: @AnatoleLucet 's idea looks good. I wasn't aware this feature should be with persist api, instead of options. (To clarify, my point was only about that `persist` middleware shouldn't have storage specific code.)
tbor00: @dai-shi  @AnatoleLucet  Hello, I don't know if you saw the example that I left you. If you want I can also make an example like the one you comment on.

[CODESANDBOX](https://yfxbv5.csb.app/)
tbor00: @AnatoleLucet @dai-shi 

I already made the last changes. It works well in both ways, which of the two do we leave? 
Also, to be honest I'm not the best with typescript. Could you give me some reference on how to type it correctly?

Example:
[codesandbox](https://2qimn3.csb.app/)
tbor00: Hi @AnatoleLucet , were you able to review the examples?
AnatoleLucet: After thinking a bit about it, i think we shouldn't include this function for the same reasons we didn't include a [`useHydration`](https://github.com/pmndrs/zustand/wiki/Persisting-the-store's-data#how-can-i-check-if-my-store-has-been-hydrated) hook.
As Daishi said, we don't want to lean into a specific context (web, mobile, server, etc.), so i'm thinking about only exposing the `getOptions` getter and leave the implementation to the user (with an example in the doc). After all, that's why we added the persist api :slightly_smiling_face: 

Thoughts @tbor00 @dai-shi?
tbor00: If it's OK, I think we can expose the getOptions and let developers make their own functions to detect changes. We could document an example of a hook for this in the documentation, what do you think? @AnatoleLucet @dai-shi 
tbor00: ready, you can check it again? @AnatoleLucet @dai-shi 

Patch:
diff --git a/docs/persisting-store-data.md b/docs/persisting-store-data.md
new file mode 100644
index 0000000..1ea4b1f
--- /dev/null
+++ b/docs/persisting-store-data.md
@@ -0,0 +1,543 @@
+# Persist middleware
+
+The persist middleware enables you to store your Zustand state in a storage (e.g. `localStorage`, `AsyncStorage`, `IndexedDB`, etc...) thus persisting it's data.
+
+Note that this middleware does support both synchronous storages (e.g. `localStorage`) and asynchronous storages (e.g. `AsyncStorage`), but using an asynchronous storage does come with a cost.
+See [Hydration and asynchronous storages](#Hydration-and-asynchronous-storages) for more details.
+
+Quick example:
+
+```ts
+import create from 'zustand'
+import { persist } from 'zustand/middleware'
+
+export const useStore = create(
+  persist(
+    (set, get) => ({
+      fishes: 0,
+      addAFish: () => set({ fishes: get().fishes + 1 }),
+    }),
+    {
+      name: 'food-storage', // name of item in the storage (must be unique)
+      getStorage: () => sessionStorage, // (optional) by default the 'localStorage' is used
+    }
+  )
+)
+```
+
+See [Options](#Options) for more details.
+
+## Options
+
+### `name`
+
+This is the only required option.
+The given name is going to be the key used to store your Zustand state in the storage, so it must be unique.
+
+### `getStorage`
+
+> Default: `() => localStorage`
+
+Enables you to use your own storage.
+Simply pass a function that returns the storage you want to use.
+
+Example:
+
+```ts
+export const useStore = create(
+  persist(
+    (set, get) => ({
+      // ...
+    }),
+    {
+      // ...
+      getStorage: () => AsyncStorage,
+    }
+  )
+)
+```
+
+The given storage must match the following interface:
+
+```ts
+interface Storage {
+  getItem: (name: string) => string | null | Promise<string | null>
+  setItem: (name: string, value: string) => void | Promise<void>
+  removeItem: (name: string) => void | Promise<void>
+}
+```
+
+### `serialize`
+
+> Schema: `(state: Object) => string | Promise<string>`
+
+> Default: `(state) => JSON.stringify(state)`
+
+Since the only way to store an object in a storage is via a string, you can use this option to give a custom function to serialize your state to a string.
+
+For example, if you want to store your state in base64:
+
+```ts
+export const useStore = create(
+  persist(
+    (set, get) => ({
+      // ...
+    }),
+    {
+      // ...
+      serialize: (state) => btoa(JSON.stringify(state)),
+    }
+  )
+)
+```
+
+Note that you would also need a custom `deserialize` function to make this work properly. See below.
+
+### `deserialize`
+
+> Schema: `(str: string) => Object | Promise<Object>`
+
+> Default: `(str) => JSON.parse(str)`
+
+If you pass a custom serialize function, you will most likely need to pass a custom deserialize function as well.
+
+To continue the example above, you could deserialize the base64 value using the following:
+
+```ts
+export const useStore = create(
+  persist(
+    (set, get) => ({
+      // ...
+    }),
+    {
+      // ...
+      deserialize: (str) => JSON.parse(atob(str)),
+    }
+  )
+)
+```
+
+### `partialize`
+
+> Schema: `(state: Object) => Object`
+
+> Default: `(state) => state`
+
+Enables you to omit some of the state's fields to be stored in the storage.
+
+You could omit multiple fields using the following:
+
+```ts
+export const useStore = create(
+  persist(
+    (set, get) => ({
+      foo: 0,
+      bar: 1,
+    }),
+    {
+      // ...
+      partialize: (state) =>
+        Object.fromEntries(
+          Object.entries(state).filter(([key]) => !['foo'].includes(key))
+        ),
+    }
+  )
+)
+```
+
+Or you could allow only specific fields using the following:
+
+```ts
+export const useStore = create(
+  persist(
+    (set, get) => ({
+      foo: 0,
+      bar: 1,
+    }),
+    {
+      // ...
+      partialize: (state) => ({ foo: state.foo }),
+    }
+  )
+)
+```
+
+### `onRehydrateStorage`
+
+> Schema: `(state: Object) => ((state?: Object, error?: Error) => void) | void`
+
+This option enables you to pass a listener function that will be called when the storage is hydrated.
+
+Example:
+
+```ts
+export const useStore = create(
+  persist(
+    (set, get) => ({
+      // ...
+    }),
+    {
+      // ...
+      onRehydrateStorage: (state) => {
+        console.log('hydration starts')
+
+        // optional
+        return (state, error) => {
+          if (error) {
+            console.log('an error happened during hydration', error)
+          } else {
+            console.log('hydration finished')
+          }
+        }
+      },
+    }
+  )
+)
+```
+
+### `version`
+
+> Schema: `number`
+
+> Default: `0`
+
+If you want to introduce a breaking change in your storage (e.g. renaming a field), you can specify a new version number.
+By default, if the version in the storage does not match the version in the code, the stored value won't be used.
+
+See the `migrate` option below for more details about handling breaking changes.
+
+### `migrate`
+
+> Schema: `(persistedState: Object, version: number) => Object | Promise<Object>`
+
+> Default: `(persistedState) => persistedState`
+
+You can use this option to handle versions migration.
+The migrate function takes the persisted state and the version number as arguments. It must return a state that is compliant to the latest version (the version in the code).
+
+For instance, if you want to rename a field, you can use the following:
+
+```ts
+export const useStore = create(
+  persist(
+    (set, get) => ({
+      newField: 0, // let's say this field was named otherwise in version 0
+    }),
+    {
+      // ...
+      version: 1, // a migration will be triggered if the version in the storage mismatches this one
+      migrate: (persistedState, version) => {
+        if (version === 0) {
+          // if the stored value is in version 0, we rename the field to the new name
+          persistedState.newField = persistedState.oldField
+          delete persistedState.oldField
+        }
+
+        return persistedState
+      },
+    }
+  )
+)
+```
+
+### `merge`
+
+> Schema: `(persistedState: Object, currentState: Object) => Object`
+
+> Default: `(persistedState, currentState) => ({ ...currentState, ...persistedState })`
+
+In some cases, you might want to use a custom merge function to merge the persisted value with the current state.
+
+By default, the middleware does a shallow merge.
+The shallow merge might not be enough if you have partially persisted nested objects.
+For instance, if the storage contains the following:
+
+```ts
+{
+  foo: {
+    bar: 0,
+  }
+}
+```
+
+But your Zustand store contains:
+
+```ts
+{
+  foo: {
+    bar: 0,
+    baz: 1,
+  }
+}
+```
+
+The shallow merge will erase the `baz` field from the `foo` object.
+One way to fix this would be to give a custom deep merge function:
+
+```ts
+export const useStore = create(
+  persist(
+    (set, get) => ({
+      foo: {
+        bar: 0,
+        baz: 1,
+      },
+    }),
+    {
+      // ...
+      merge: (persistedState, currentState) =>
+        deepMerge(currentState, persistedState),
+    }
+  )
+)
+```
+
+## API
+
+> Version: >=3.6.3
+
+The persist api enables you to do numbers of interactions with the persist middleware from inside or outside a React component.
+
+### `getOptions`
+
+> Schema: `() => Partial<PersistOptions>`
+
+This method can you get the options of the middleware.
+
+For example, it can be used to obtain the storage name:
+
+```ts
+useStore.persist.getOptions().name
+```
+
+### `setOptions`
+
+> Schema: `(newOptions: PersistOptions) => void`
+
+This method enables you to change the middleware options. Note that the new options will be merged with the current ones.
+
+For instance, this can be used to change the storage name:
+
+```ts
+useStore.persist.setOptions({
+  name: 'new-name',
+})
+```
+
+Or even to change the storage engine:
+
+```ts
+useStore.persist.setOptions({
+  getStorage: () => sessionStorage,
+})
+```
+
+### `clearStorage`
+
+> Schema: `() => void`
+
+This can be used to fully clear the persisted value in the storage.
+
+```ts
+useStore.persist.clearStorage()
+```
+
+### `rehydrate`
+
+> Schema: `() => Promise<void>`
+
+In some cases, you might want to trigger a rehydration manually.
+This can be done by calling the `rehydrate` method.
+
+```ts
+await useStore.persist.rehydrate()
+```
+
+### `hasHydrated`
+
+> Schema: `() => boolean`
+
+This is a non-reactive getter to know if the storage has been hydrated (note that this does update when calling `useStore.persist.rehydrate()`).
+
+```ts
+useStore.persist.hasHydrated()
+```
+
+### `onHydrate`
+
+> Schema: `(listener: (state) => void) => () => void`
+
+The given listener will be called when the hydration process starts.
+
+```ts
+const unsub = useStore.persist.onHydrate((state) => {
+  console.log('hydration starts')
+})
+
+// later on...
+unsub()
+```
+
+### `onFinishHydration`
+
+> Schema: `(listener: (state) => void) => () => void`
+
+The given listener will be called when the hydration process ends.
+
+```ts
+const unsub = useStore.persist.onFinishHydration((state) => {
+  console.log('hydration finished')
+})
+
+// later on...
+unsub()
+```
+
+## Hydration and asynchronous storages
+
+To explain what's the "cost" of asynchronous storages, you need to understand what's hydration.
+In a nutshell, hydration is the process of retrieving the persisted state from the storage and merging it with the current state.
+
+The persist middleware does two kinds of hydration: synchronous and asynchronous.
+If the given storage is synchronous (e.g. `localStorage`), hydration will be done synchronously. On the other hand, if the given storage is asynchronous (e.g. `AsyncStorage`), hydration will be done ... 🥁 asynchronously.
+
+But what's the catch?
+With synchronous hydration, the Zustand store will have been hydrated at its creation.
+While with asynchronous hydration, the Zustand store will be hydrated later on, in a microtask.
+
+Why does it matter?
+Asynchronous hydration can cause some unexpected behaviors.
+For instance, if you use Zustand in a React app, the store will _not_ be hydrated at the initial render. In cases where you app depends on the persisted value at page load, you might want to wait until the store has been hydrated before showing anything (e.g. your app might think the user is not logged in because it's the default, while in reality the store has not been hydrated yet).
+
+If your app does depends on the persisted state at page load, see [How can I check if my store has been hydrated?](#how-can-I-check-if-my-store-has-been-hydrated) in the Q/A.
+
+## Q/A
+
+### How can I check if my store has been hydrated?
+
+There's a fiew different ways to do this.
+
+You can use the `onRehydrateStorage` option to update a field in the store:
+
+```ts
+const useStore = create(
+  persist(
+    (set, get) => ({
+      // ...
+      _hasHydrated: false,
+      setHasHydrated: (state) => {
+        set({
+          _hasHydrated: state
+        });
+      }
+    }),
+    {
+      // ...
+      onRehydrateStorage: () => (state) => {
+        state.setHasHydrated(true)
+      }
+    }
+  )
+);
+
+export default function App() {
+  const hasHydrated = useStore(state => state._hasHydrated);
+
+  if (!hasHydrated) {
+    return <p>Loading...</p>
+  }
+
+  return (
+    // ...
+  );
+}
+```
+
+You can also create a custom `useHydration` hook:
+
+```ts
+const useStore = create(persist(...))
+
+const useHydration = () => {
+  const [hydrated, setHydrated] = useState(useStore.persist.hasHydrated)
+
+  useEffect(() => {
+    const unsubHydrate = useStore.persist.onHydrate(() => setHydrated(false)) // Note: this is just in case you want to take into account manual rehydrations. You can remove this if you don't need it/don't want it.
+    const unsubFinishHydration = useStore.persist.onFinishHydration(() => setHydrated(true))
+
+    setHydrated(useStore.persist.hasHydrated())
+
+    return () => {
+      unsubHydrate()
+      unsubFinishHydration()
+    }
+  }, [])
+
+  return hydrated
+}
+```
+
+### How can I use a custom storage engine?
+
+If the storage you want to use does not match the expected API, you can create your own storage:
+
+```ts
+import create from 'zustand'
+import { persist, StateStorage } from 'zustand/middleware'
+import { get, set, del } from 'idb-keyval' // can use anything: IndexedDB, Ionic Storage, etc.
+
+// Custom storage object
+const storage: StateStorage = {
+  getItem: async (name: string): Promise<string | null> => {
+    console.log(name, 'has been retrieved')
+    return (await get(name)) || null
+  },
+  setItem: async (name: string, value: string): Promise<void> => {
+    console.log(name, 'with value', value, 'has been saved')
+    await set(name, value)
+  },
+  removeItem: async (name: string): Promise<void> => {
+    console.log(name, 'has been deleted')
+    await del(name)
+  },
+}
+
+export const useStore = create(
+  persist(
+    (set, get) => ({
+      fishes: 0,
+      addAFish: () => set({ fishes: get().fishes + 1 }),
+    }),
+    {
+      name: 'food-storage', // unique name
+      getStorage: () => storage,
+    }
+  )
+)
+```
+
+### How can I rehydrate on storage event?
+
+You can use the `persist` api to create your own implementation, similar to what we see below
+
+```ts
+
+type StoreWithPersist = Mutate<StoreApi<State>, [["zustand/persist", unknown]]>
+
+export const withStorageDOMEvents = (store: StoreWithPersist) => {
+  const storageEventCallback = (e: StorageEvent) => {
+    if (e.key === store.persist.getOptions().name && e.newValue) {
+      store.persist.rehydrate()
+    }
+  }
+
+  window.addEventListener('storage', storageEventCallback)
+
+  return () => {
+    window.removeEventListener('storage', storageEventCallback)
+  }
+}
+
+const useStore = create(persist(...))
+withStorageDOMEvents(useStore)
+```
diff --git a/docs/typescript.md b/docs/typescript.md
index 41a6ce8..81a8dab 100644
--- a/docs/typescript.md
+++ b/docs/typescript.md
@@ -345,40 +345,7 @@ const useStore = create<
 })))
 ```
 
-### Independent slices pattern
-
-```ts
-import create, { StateCreator } from 'zustand'
-
-interface BearSlice {
-  bears: number
-  addBear: () => void
-}
-const createBearSlice: StateCreator<BearSlice, [], []> = (set) => ({
-  bears: 0,
-  addBear: () => set((state) => ({ bears: state.bears + 1 })),
-})
-
-interface FishSlice {
-  fishes: number
-  addFish: () => void
-}
-const createFishSlice: StateCreator<FishSlice, [], []> = (set) => ({
-  fishes: 0,
-  addFish: () => set((state) => ({ fishes: state.fishes + 1 })),
-})
-
-const useStore = create<BearSlice & FishSlice>()((...a) => ({
-  ...createBearSlice(...a),
-  ...createFishSlice(...a),
-}))
-```
-
-If you have some middlewares then replace `StateCreator<MySlice, [], []>` with `StateCreator<MySlice, Mutators, []>`. Eg if you're using `devtools` then it'll be `StateCreator<MySlice, [["zustand/devtools", never]], []>`. See the ["Middlewares and their mutators reference"](#middlewares-and-their-mutators-reference) section for a list of all mutators.
-
-Also you can even write `StateCreator<MySlice>` instead of `StateCreator<MySlice, [], []>` as the second and third parameter have `[]` as their default value.
-
-### Interdependent slices pattern
+### Slices pattern
 
 ```ts
 import create, { StateCreator } from 'zustand'
diff --git a/examples/config-overrides.js b/examples/config-overrides.js
index eefd739..8ffd96e 100644
--- a/examples/config-overrides.js
+++ b/examples/config-overrides.js
@@ -1,11 +1,6 @@
-const { addWebpackPlugin, addWebpackAlias, override } = require('customize-cra')
+const { override } = require('customize-cra')
 const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin
-const path = require('path')
 
 module.exports = override(
-  addWebpackAlias({
-    three$: path.resolve('./src/utils/three.js'),
-    '../../../build/three.module.js': path.resolve('./src/utils/three.js'),
-  }),
   //addWebpackPlugin(new BundleAnalyzerPlugin())
 )
diff --git a/examples/package.json b/examples/package.json
index fecb87f..2dcd4ae 100644
--- a/examples/package.json
+++ b/examples/package.json
@@ -3,15 +3,15 @@
   "version": "0.1.0",
   "private": true,
   "dependencies": {
-    "drei": "^0.0.72",
+    "@react-three/drei": "^9.17.1",
+    "@react-three/fiber": "^8.1.0",
+    "@react-three/postprocessing": "^2.4.6",
+    "meshline": "^2.0.4",
     "prism-react-renderer": "^1.2.1",
     "prismjs": "1.23.0",
     "react": "^18.2.0",
     "react-dom": "^18.2.0",
-    "react-postprocessing": "^1.1.4",
-    "react-three-fiber": "^4.2.20",
-    "three": "^0.120.1",
-    "threejs-meshline": "^2.0.11",
+    "three": "^0.141.0",
     "zustand": "^3.0.0"
   },
   "scripts": {
diff --git a/examples/src/components/Backdrop.js b/examples/src/components/Backdrop.js
index 86cba2b..65056e9 100644
--- a/examples/src/components/Backdrop.js
+++ b/examples/src/components/Backdrop.js
@@ -1,5 +1,5 @@
 import { Suspense, useRef } from 'react'
-import { Canvas } from 'react-three-fiber'
+import { Canvas } from '@react-three/fiber'
 import Effects from './Effects'
 import Scene from './Scene'
 
diff --git a/examples/src/components/Effects.js b/examples/src/components/Effects.js
index fa8e9aa..afcf1dc 100644
--- a/examples/src/components/Effects.js
+++ b/examples/src/components/Effects.js
@@ -1,6 +1,6 @@
 import { forwardRef } from 'react'
-import { useThree } from 'react-three-fiber'
-import { EffectComposer, DepthOfField, Vignette } from 'react-postprocessing'
+import { useThree } from '@react-three/fiber'
+import { EffectComposer, DepthOfField, Vignette } from '@react-three/postprocessing'
 
 const Effects = forwardRef((props, ref) => {
   const {
@@ -8,7 +8,7 @@ const Effects = forwardRef((props, ref) => {
   } = useThree()
   return (
     <EffectComposer multisampling={0}>
-      <DepthOfField ref={ref} bokehScale={4} focalLength={0.1} width={width / 2} height={height / 2} />
+      <DepthOfField ref={ref} bokehScale={4} focalLength={0.1} resolutionX={width * 3} resolutionY={height * 3} />
       <Vignette />
     </EffectComposer>
   )
diff --git a/examples/src/components/Fireflies.js b/examples/src/components/Fireflies.js
index 97d7be1..e82635e 100644
--- a/examples/src/components/Fireflies.js
+++ b/examples/src/components/Fireflies.js
@@ -1,7 +1,7 @@
 import * as THREE from 'three'
 import { useRef, useMemo } from 'react'
-import { extend, useFrame } from 'react-three-fiber'
-import * as meshline from 'threejs-meshline'
+import { extend, useFrame } from '@react-three/fiber'
+import * as meshline from 'meshline'
 
 extend(meshline)
 
@@ -12,7 +12,7 @@ function Fatline({ curve, width, color }) {
   useFrame((state, delta) => (material.current.uniforms.dashOffset.value -= delta / 100))
   return (
     <mesh>
-      <meshLine attach="geometry" vertices={curve} />
+      <meshLine attach="geometry" points={curve} />
       <meshLineMaterial
         attach="material"
         ref={material}
diff --git a/examples/src/components/Scene.js b/examples/src/components/Scene.js
index 0e561dd..8f65ac1 100644
--- a/examples/src/components/Scene.js
+++ b/examples/src/components/Scene.js
@@ -1,7 +1,7 @@
 import * as THREE from 'three'
 import { useRef, useState } from 'react'
-import { Plane, useAspect, useTextureLoader } from 'drei'
-import { useFrame } from 'react-three-fiber'
+import { Plane, useAspect, useTexture } from '@react-three/drei'
+import { useFrame } from '@react-three/fiber'
 import Fireflies from './Fireflies'
 import bgUrl from '../resources/bg.jpg'
 import starsUrl from '../resources/stars.png'
@@ -12,9 +12,9 @@ import leaves2Url from '../resources/leaves2.png'
 import '../materials/layerMaterial'
 
 export default function Scene({ dof }) {
-  const scaleN = useAspect('cover', 1600, 1000, 0.21)
-  const scaleW = useAspect('cover', 2200, 1000, 0.21)
-  const textures = useTextureLoader([bgUrl, starsUrl, groundUrl, bearUrl, leaves1Url, leaves2Url])
+  const scaleN = useAspect(16, 10, 1.05)
+  const scaleW = useAspect(22, 10, 1.05)
+  const textures = useTexture([bgUrl, starsUrl, groundUrl, bearUrl, leaves1Url, leaves2Url])
   const subject = useRef()
   const group = useRef()
   const layersRef = useRef([])
@@ -32,10 +32,10 @@ export default function Scene({ dof }) {
 
   useFrame((state, delta) => {
     dof.current.target = focusVector.lerp(subject.current.position, 0.05)
-    movementVector.lerp(tempVector.set(state.mouse.x, state.mouse.y * 0.2, 0), 0.2)
-    group.current.position.x = THREE.MathUtils.lerp(group.current.position.x, state.mouse.x * 20, 0.2)
-    group.current.rotation.x = THREE.MathUtils.lerp(group.current.rotation.x, state.mouse.y / 10, 0.2)
-    group.current.rotation.y = THREE.MathUtils.lerp(group.current.rotation.y, -state.mouse.x / 2, 0.2)
+    movementVector.lerp(tempVector.set(state.pointer.x, state.pointer.y * 0.2, 0), 0.2)
+    group.current.position.x = THREE.MathUtils.lerp(group.current.position.x, state.pointer.x * 20, 0.2)
+    group.current.rotation.x = THREE.MathUtils.lerp(group.current.rotation.x, state.pointer.y / 10, 0.2)
+    group.current.rotation.y = THREE.MathUtils.lerp(group.current.rotation.y, -state.pointer.x / 2, 0.2)
     layersRef.current[4].uniforms.time.value = layersRef.current[5].uniforms.time.value += delta
   }, 1)
 
diff --git a/examples/src/materials/layerMaterial.js b/examples/src/materials/layerMaterial.js
index d392628..67c7346 100644
--- a/examples/src/materials/layerMaterial.js
+++ b/examples/src/materials/layerMaterial.js
@@ -1,5 +1,5 @@
-import { shaderMaterial } from 'drei'
-import { extend } from 'react-three-fiber'
+import { shaderMaterial } from '@react-three/drei'
+import { extend } from '@react-three/fiber'
 
 const LayerMaterial = shaderMaterial(
   { textr: null, movementVector: [0, 0, 0], scaleFactor: 1, factor: 0, wiggle: 0, time: 0 },
@@ -32,7 +32,7 @@ const LayerMaterial = shaderMaterial(
       vec2 uv = vUv / scaleFactor + movementVector.xy * factor;
       vec4 color = texture2D(textr, uv);
       if (color.a < 0.1) discard;
-      gl_FragColor = vec4(color.rgb, .1);
+      gl_FragColor = color;
     }`
 )
 
diff --git a/examples/src/utils/three.js b/examples/src/utils/three.js
deleted file mode 100644
index 78a9e42..0000000
--- a/examples/src/utils/three.js
+++ /dev/null
@@ -1,204 +0,0 @@
-//import { REVISION } from 'three/src/constants.js'
-
-export { WebGLMultisampleRenderTarget } from 'three/src/renderers/WebGLMultisampleRenderTarget.js'
-//export { WebGLCubeRenderTarget } from 'three/src/renderers/WebGLCubeRenderTarget.js'
-export { WebGLRenderTarget } from 'three/src/renderers/WebGLRenderTarget.js'
-export { WebGLRenderer } from 'three/src/renderers/WebGLRenderer.js'
-export { ShaderLib } from 'three/src/renderers/shaders/ShaderLib.js'
-export { UniformsLib } from 'three/src/renderers/shaders/UniformsLib.js'
-export { UniformsUtils } from 'three/src/renderers/shaders/UniformsUtils.js'
-export { ShaderChunk } from 'three/src/renderers/shaders/ShaderChunk.js'
-//export { FogExp2 } from 'three/src/scenes/FogExp2.js'
-export { Fog } from 'three/src/scenes/Fog.js'
-export { Scene } from 'three/src/scenes/Scene.js'
-//export { Sprite } from 'three/src/objects/Sprite.js'
-//export { LOD } from 'three/src/objects/LOD.js'
-//export { SkinnedMesh } from 'three/src/objects/SkinnedMesh.js'
-export class SkinnedMesh {}
-//export { Skeleton } from 'three/src/objects/Skeleton.js'
-export class Skeleton {}
-//export { Bone } from 'three/src/objects/Bone.js'
-export class Bone {}
-export { Mesh } from 'three/src/objects/Mesh.js'
-//export { InstancedMesh } from 'three/src/objects/InstancedMesh.js'
-//export { LineSegments } from 'three/src/objects/LineSegments.js'
-export class LineSegments {}
-//export { LineLoop } from 'three/src/objects/LineLoop.js'
-export class LineLoop {}
-//export { Line } from 'three/src/objects/Line.js'
-export class Line {}
-//export { Points } from 'three/src/objects/Points.js'
-export class Points {}
-export { Group } from 'three/src/objects/Group.js'
-//export { VideoTexture } from 'three/src/textures/VideoTexture.js'
-//export { DataTexture } from 'three/src/textures/DataTexture.js'
-export class DataTexture {}
-//export { DataTexture2DArray } from 'three/src/textures/DataTexture2DArray.js'
-//export { DataTexture3D } from 'three/src/textures/DataTexture3D.js'
-//export { CompressedTexture } from 'three/src/textures/CompressedTexture.js'
-//export { CubeTexture } from 'three/src/textures/CubeTexture.js'
-//export { CanvasTexture } from 'three/src/textures/CanvasTexture.js'
-export class CanvasTexture {}
-export { DepthTexture } from 'three/src/textures/DepthTexture.js'
-export { Texture } from 'three/src/textures/Texture.js'
-export * from 'three/src/geometries/Geometries.js'
-//export * from 'three/src/materials/Materials.js'
-export { Material } from 'three/src/materials/Material.js'
-//export { MeshStandardMaterial } from 'three/src/materials/MeshStandardMaterial.js'
-export { ShaderMaterial } from 'three/src/materials/ShaderMaterial.js'
-export { MeshNormalMaterial } from 'three/src/materials/MeshNormalMaterial.js'
-export class LineBasicMaterial {}
-export class MeshBasicMaterial {}
-export class MeshDepthMaterial {}
-export class MeshPhysicalMaterial {}
-export class MeshStandardMaterial {}
-export class PointsMaterial {}
-//export { AnimationLoader } from 'three/src/loaders/AnimationLoader.js'
-//export { CompressedTextureLoader } from 'three/src/loaders/CompressedTextureLoader.js'
-//export { DataTextureLoader } from 'three/src/loaders/DataTextureLoader.js'
-//export { CubeTextureLoader } from 'three/src/loaders/CubeTextureLoader.js'
-export class CubeTextureLoader {}
-export { TextureLoader } from 'three/src/loaders/TextureLoader.js'
-//export { ObjectLoader } from 'three/src/loaders/ObjectLoader.js'
-//export { MaterialLoader } from 'three/src/loaders/MaterialLoader.js'
-//export { BufferGeometryLoader } from 'three/src/loaders/BufferGeometryLoader.js'
-export { /*DefaultLoadingManager,*/ LoadingManager } from 'three/src/loaders/LoadingManager.js'
-//export { ImageLoader } from 'three/src/loaders/ImageLoader.js'
-//export { ImageBitmapLoader } from 'three/src/loaders/ImageBitmapLoader.js'
-export class ImageBitmapLoader {}
-//export { FontLoader } from 'three/src/loaders/FontLoader.js'
-export { FileLoader } from 'three/src/loaders/FileLoader.js'
-export { Loader } from 'three/src/loaders/Loader.js'
-export { LoaderUtils } from 'three/src/loaders/LoaderUtils.js'
-//export { Cache } from 'three/src/loaders/Cache.js'
-//export { AudioLoader } from 'three/src/loaders/AudioLoader.js'
-export class AudioLoader {}
-export { SpotLightShadow } from 'three/src/lights/SpotLightShadow.js'
-export { SpotLight } from 'three/src/lights/SpotLight.js'
-export { PointLight } from 'three/src/lights/PointLight.js'
-//export { RectAreaLight } from 'three/src/lights/RectAreaLight.js'
-//export { HemisphereLight } from 'three/src/lights/HemisphereLight.js'
-//export { HemisphereLightProbe } from 'three/src/lights/HemisphereLightProbe.js'
-export { DirectionalLightShadow } from 'three/src/lights/DirectionalLightShadow.js'
-export { DirectionalLight } from 'three/src/lights/DirectionalLight.js'
-//export { AmbientLight } from 'three/src/lights/AmbientLight.js'
-//export { AmbientLightProbe } from 'three/src/lights/AmbientLightProbe.js'
-export { LightShadow } from 'three/src/lights/LightShadow.js'
-export { Light } from 'three/src/lights/Light.js'
-//export { LightProbe } from 'three/src/lights/LightProbe.js'
-//export { StereoCamera } from 'three/src/cameras/StereoCamera.js'
-export { PerspectiveCamera } from 'three/src/cameras/PerspectiveCamera.js'
-export { OrthographicCamera } from 'three/src/cameras/OrthographicCamera.js'
-//export { CubeCamera } from 'three/src/cameras/CubeCamera.js'
-//export { ArrayCamera } from 'three/src/cameras/ArrayCamera.js'
-export { Camera } from 'three/src/cameras/Camera.js'
-//export { AudioListener } from 'three/src/audio/AudioListener.js'
-export class AudioListener {}
-//export { PositionalAudio } from 'three/src/audio/PositionalAudio.js'
-//export { AudioContext } from 'three/src/audio/AudioContext.js'
-//export { AudioAnalyser } from 'three/src/audio/AudioAnalyser.js'
-//export { Audio } from 'three/src/audio/Audio.js'
-//export { VectorKeyframeTrack } from 'three/src/animation/tracks/VectorKeyframeTrack.js'
-export class VectorKeyframeTrack {}
-//export { StringKeyframeTrack } from 'three/src/animation/tracks/StringKeyframeTrack.js'
-//export { QuaternionKeyframeTrack } from 'three/src/animation/tracks/QuaternionKeyframeTrack.js'
-export class QuaternionKeyframeTrack {}
-//export { NumberKeyframeTrack } from 'three/src/animation/tracks/NumberKeyframeTrack.js'
-export class NumberKeyframeTrack {}
-//export { ColorKeyframeTrack } from 'three/src/animation/tracks/ColorKeyframeTrack.js'
-//export { BooleanKeyframeTrack } from 'three/src/animation/tracks/BooleanKeyframeTrack.js'
-//export { PropertyMixer } from 'three/src/animation/PropertyMixer.js'
-export { PropertyBinding } from 'three/src/animation/PropertyBinding.js'
-//export { KeyframeTrack } from 'three/src/animation/KeyframeTrack.js'
-//export { AnimationUtils } from 'three/src/animation/AnimationUtils.js'
-//export { AnimationObjectGroup } from 'three/src/animation/AnimationObjectGroup.js'
-//export { AnimationMixer } from 'three/src/animation/AnimationMixer.js'
-//export { AnimationClip } from 'three/src/animation/AnimationClip.js'
-export class AnimationClip {}
-export { Uniform } from 'three/src/core/Uniform.js'
-//export { InstancedBufferGeometry } from 'three/src/core/InstancedBufferGeometry.js'
-export class InstancedBufferGeometry {}
-export { BufferGeometry } from 'three/src/core/BufferGeometry.js'
-export class BoxBufferGeometry {}
-export class CylinderBufferGeometry {}
-export class OctahedronBufferGeometry {}
-export class SphereBufferGeometry {}
-export class TorusBufferGeometry {}
-export class WireframeGeometry {}
-//export { Geometry } from 'three/src/core/Geometry.js'
-export { PlaneBufferGeometry } from 'three/src/geometries/PlaneGeometry.js'
-//export { InterleavedBufferAttribute } from 'three/src/core/InterleavedBufferAttribute.js'
-export class InterleavedBufferAttribute {}
-//export { InstancedInterleavedBuffer } from 'three/src/core/InstancedInterleavedBuffer.js'
-export class InstancedInterleavedBuffer {}
-//export { InterleavedBuffer } from 'three/src/core/InterleavedBuffer.js'
-export class InterleavedBuffer {}
-//export { InstancedBufferAttribute } from 'three/src/core/InstancedBufferAttribute.js'
-export * from 'three/src/core/BufferAttribute.js'
-export { Face3 } from 'three/src/core/Face3.js'
-export { Object3D } from 'three/src/core/Object3D.js'
-//export { Raycaster } from 'three/src/core/Raycaster.js'
-export class Raycaster {
-  setFromCamera() {}
-  intersectObjects() {
-    return []
-  }
-}
-export { Layers } from 'three/src/core/Layers.js'
-export { EventDispatcher } from 'three/src/core/EventDispatcher.js'
-export { Clock } from 'three/src/core/Clock.js'
-//export { QuaternionLinearInterpolant } from 'three/src/math/interpolants/QuaternionLinearInterpolant.js'
-//export { LinearInterpolant } from 'three/src/math/interpolants/LinearInterpolant.js'
-//export { DiscreteInterpolant } from 'three/src/math/interpolants/DiscreteInterpolant.js'
-//export { CubicInterpolant } from 'three/src/math/interpolants/CubicInterpolant.js'
-//export { Interpolant } from 'three/src/math/Interpolant.js'
-export class Interpolant {}
-//export { Triangle } from 'three/src/math/Triangle.js'
-export { MathUtils } from 'three/src/math/MathUtils.js'
-//export { Spherical } from 'three/src/math/Spherical.js'
-export class Spherical {}
-//export { Cylindrical } from 'three/src/math/Cylindrical.js'
-export { Plane } from 'three/src/math/Plane.js'
-//export { Frustum } from 'three/src/math/Frustum.js'
-export { Sphere } from 'three/src/math/Sphere.js'
-//export { Ray } from 'three/src/math/Ray.js'
-export class Ray {}
-export { Matrix4 } from 'three/src/math/Matrix4.js'
-export { Matrix3 } from 'three/src/math/Matrix3.js'
-export { Box3 } from 'three/src/math/Box3.js'
-export { Box2 } from 'three/src/math/Box2.js'
-export { Line3 } from 'three/src/math/Line3.js'
-export { Euler } from 'three/src/math/Euler.js'
-export { Vector4 } from 'three/src/math/Vector4.js'
-export { Vector3 } from 'three/src/math/Vector3.js'
-export { Vector2 } from 'three/src/math/Vector2.js'
-export { Quaternion } from 'three/src/math/Quaternion.js'
-export { Color } from 'three/src/math/Color.js'
-//export { SphericalHarmonics3 } from 'three/src/math/SphericalHarmonics3.js'
-//export { ImmediateRenderObject } from 'three/src/extras/objects/ImmediateRenderObject.js'
-//export { SpotLightHelper } from 'three/src/helpers/SpotLightHelper.js'
-//export { SkeletonHelper } from 'three/src/helpers/SkeletonHelper.js'
-//export { PointLightHelper } from 'three/src/helpers/PointLightHelper.js'
-//export { HemisphereLightHelper } from 'three/src/helpers/HemisphereLightHelper.js'
-//export { GridHelper } from 'three/src/helpers/GridHelper.js'
-//export { PolarGridHelper } from 'three/src/helpers/PolarGridHelper.js'
-//export { DirectionalLightHelper } from 'three/src/helpers/DirectionalLightHelper.js'
-//export { CameraHelper } from 'three/src/helpers/CameraHelper.js'
-//export { BoxHelper } from 'three/src/helpers/BoxHelper.js'
-//export { Box3Helper } from 'three/src/helpers/Box3Helper.js'
-//export { PlaneHelper } from 'three/src/helpers/PlaneHelper.js'
-//export { ArrowHelper } from 'three/src/helpers/ArrowHelper.js'
-//export { AxesHelper } from 'three/src/helpers/AxesHelper.js'
-//export * from 'three/src/extras/curves/Curves.js'
-export { CatmullRomCurve3 } from 'three/src/extras/curves/CatmullRomCurve3'
-//export { Shape } from 'three/src/extras/core/Shape.js'
-//export { Path } from 'three/src/extras/core/Path.js'
-//export { ShapePath } from 'three/src/extras/core/ShapePath.js'
-//export { Font } from 'three/src/extras/core/Font.js'
-//export { CurvePath } from 'three/src/extras/core/CurvePath.js'
-//export { Curve } from 'three/src/extras/core/Curve.js'
-//export { ImageUtils } from 'three/src/extras/ImageUtils.js'
-//export { ShapeUtils } from 'three/src/extras/ShapeUtils.js'
-//export { PMREMGenerator } from 'three/src/extras/PMREMGenerator.js'
-//export { WebGLUtils } from 'three/src/renderers/webgl/WebGLUtils.js'
-export * from 'three/src/constants.js'
diff --git a/examples/yarn.lock b/examples/yarn.lock
index 808f43c..00829eb 100644
--- a/examples/yarn.lock
+++ b/examples/yarn.lock
@@ -1048,14 +1048,14 @@
     core-js-pure "^3.0.0"
     regenerator-runtime "^0.13.4"
 
-"@babel/runtime@^7.10.2", "@babel/runtime@^7.10.3", "@babel/runtime@^7.10.5", "@babel/runtime@^7.9.2":
+"@babel/runtime@^7.10.2", "@babel/runtime@^7.10.3", "@babel/runtime@^7.9.2":
   version "7.11.2"
   resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.11.2.tgz#f549c13c754cc40b87644b9fa9f09a6a95fe0736"
   integrity sha512-TeWkU52so0mPtDcaCTxNBI/IHiz0pZgr8VEFqXFtZWpYD08ZB6FaSwVAS8MKRQAP3bYKiVjwysOJgMFY28o6Tw==
   dependencies:
     regenerator-runtime "^0.13.4"
 
-"@babel/runtime@^7.11.2", "@babel/runtime@^7.12.5", "@babel/runtime@^7.16.3", "@babel/runtime@^7.18.3", "@babel/runtime@^7.8.4":
+"@babel/runtime@^7.11.2", "@babel/runtime@^7.12.5", "@babel/runtime@^7.16.3", "@babel/runtime@^7.16.7", "@babel/runtime@^7.17.8", "@babel/runtime@^7.18.3", "@babel/runtime@^7.8.4":
   version "7.18.6"
   resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.18.6.tgz#6a1ef59f838debd670421f8c7f2cbb8da9751580"
   integrity sha512-t9wi7/AW6XtKahAe20Yw0/mMljKq0B1r2fPdvaAdV/KPDZewFXdaaa6K7lxmZBZ8FBNpCiAT6iHPmd6QO9bKfQ==
@@ -1100,6 +1100,33 @@
   resolved "https://registry.yarnpkg.com/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz#75a2e8b51cb758a7553d6804a5932d7aace75c39"
   integrity sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==
 
+"@chevrotain/cst-dts-gen@^10.1.2":
+  version "10.1.2"
+  resolved "https://registry.yarnpkg.com/@chevrotain/cst-dts-gen/-/cst-dts-gen-10.1.2.tgz#4ee6eff237bb47f4990cfb76c18ee2e71237929c"
+  integrity sha512-E/XrL0QlzExycPzwhOEZGVOheJ/Clr5uNv3oCds88MiNqEmg3UU1iauZk7DhjsUo3jgEW4lf0I5HRl7/HC5ZkQ==
+  dependencies:
+    "@chevrotain/gast" "^10.1.2"
+    "@chevrotain/types" "^10.1.2"
+    lodash "4.17.21"
+
+"@chevrotain/gast@^10.1.2":
+  version "10.1.2"
+  resolved "https://registry.yarnpkg.com/@chevrotain/gast/-/gast-10.1.2.tgz#91d5b342480d7532118a6cf3958955f86c9cc03e"
+  integrity sha512-er+TcxUOMuGOPoiOq8CJsRm92zGE4YPIYtyxJfxoVwVgtj4AMrPNCmrHvYaK/bsbt2DaDuFdcbbAfM9bcBXW6Q==
+  dependencies:
+    "@chevrotain/types" "^10.1.2"
+    lodash "4.17.21"
+
+"@chevrotain/types@^10.1.2":
+  version "10.1.2"
+  resolved "https://registry.yarnpkg.com/@chevrotain/types/-/types-10.1.2.tgz#f4caa373b1cd14d13ecb61c77dfee2456eef1ab3"
+  integrity sha512-4qF9SmmWKv8AIG/3d+71VFuqLumNCQTP5GoL0CW6x7Ay2OdXm6FUgWFLTMneGUjYUk2C+MSCf7etQfdq3LEr1A==
+
+"@chevrotain/utils@^10.1.2":
+  version "10.1.2"
+  resolved "https://registry.yarnpkg.com/@chevrotain/utils/-/utils-10.1.2.tgz#d2fb7b968141139e5c2419553e5295382c265e7d"
+  integrity sha512-bbZIpW6fdyf7FMaeDmw3cBbkTqsecxEkwlVKgVfqqXWBPLH6azxhPA2V9F7OhoZSVrsnMYw7QuyK6qutXPjEew==
+
 "@csstools/normalize.css@*":
   version "12.0.0"
   resolved "https://registry.yarnpkg.com/@csstools/normalize.css/-/normalize.css-12.0.0.tgz#a9583a75c3f150667771f30b60d9f059473e62c4"
@@ -1521,11 +1548,6 @@
     "@jridgewell/resolve-uri" "^3.0.3"
     "@jridgewell/sourcemap-codec" "^1.4.10"
 
-"@juggle/resize-observer@^3.1.3":
-  version "3.2.0"
-  resolved "https://registry.yarnpkg.com/@juggle/resize-observer/-/resize-observer-3.2.0.tgz#5e0b448d27fe3091bae6216456512c5904d05661"
-  integrity sha512-fsLxt0CHx2HCV9EL8lDoVkwHffsA0snUpddYjdLyXcG5E41xaamn9ZyQqOE9TUJdrRlH8/hjIf+UdOdDeKCUgg==
-
 "@leichtgewicht/ip-codec@^2.0.1":
   version "2.0.4"
   resolved "https://registry.yarnpkg.com/@leichtgewicht/ip-codec/-/ip-codec-2.0.4.tgz#b2ac626d6cb9c8718ab459166d4bb405b8ffa78b"
@@ -1567,6 +1589,98 @@
     schema-utils "^3.0.0"
     source-map "^0.7.3"
 
+"@react-spring/animated@~9.4.5":
+  version "9.4.5"
+  resolved "https://registry.yarnpkg.com/@react-spring/animated/-/animated-9.4.5.tgz#dd9921c716a4f4a3ed29491e0c0c9f8ca0eb1a54"
+  integrity sha512-KWqrtvJSMx6Fj9nMJkhTwM9r6LIriExDRV6YHZV9HKQsaolUFppgkOXpC+rsL1JEtEvKv6EkLLmSqHTnuYjiIA==
+  dependencies:
+    "@react-spring/shared" "~9.4.5"
+    "@react-spring/types" "~9.4.5"
+
+"@react-spring/core@~9.4.5":
+  version "9.4.5"
+  resolved "https://registry.yarnpkg.com/@react-spring/core/-/core-9.4.5.tgz#4616e1adc18dd10f5731f100ebdbe9518b89ba3c"
+  integrity sha512-83u3FzfQmGMJFwZLAJSwF24/ZJctwUkWtyPD7KYtNagrFeQKUH1I05ZuhmCmqW+2w1KDW1SFWQ43RawqfXKiiQ==
+  dependencies:
+    "@react-spring/animated" "~9.4.5"
+    "@react-spring/rafz" "~9.4.5"
+    "@react-spring/shared" "~9.4.5"
+    "@react-spring/types" "~9.4.5"
+
+"@react-spring/rafz@~9.4.5":
+  version "9.4.5"
+  resolved "https://registry.yarnpkg.com/@react-spring/rafz/-/rafz-9.4.5.tgz#84f809f287f2a66bbfbc66195db340482f886bd7"
+  integrity sha512-swGsutMwvnoyTRxvqhfJBtGM8Ipx6ks0RkIpNX9F/U7XmyPvBMGd3GgX/mqxZUpdlsuI1zr/jiYw+GXZxAlLcQ==
+
+"@react-spring/shared@~9.4.5":
+  version "9.4.5"
+  resolved "https://registry.yarnpkg.com/@react-spring/shared/-/shared-9.4.5.tgz#4c3ad817bca547984fb1539204d752a412a6d829"
+  integrity sha512-JhMh3nFKsqyag0KM5IIM8BQANGscTdd0mMv3BXsUiMZrcjQTskyfnv5qxEeGWbJGGar52qr5kHuBHtCjQOzniA==
+  dependencies:
+    "@react-spring/rafz" "~9.4.5"
+    "@react-spring/types" "~9.4.5"
+
+"@react-spring/three@^9.3.1":
+  version "9.4.5"
+  resolved "https://registry.yarnpkg.com/@react-spring/three/-/three-9.4.5.tgz#bd5b7844fde3338b05920654d81f127a5760f057"
+  integrity sha512-mArxfIhg9kyFL/8Y09TarS/ZKLd/qAWS4T3Ro/B46ILPfPnoPywDdw9/rknZihy/tslnviCgMrB4pZ29X1Dfxw==
+  dependencies:
+    "@react-spring/animated" "~9.4.5"
+    "@react-spring/core" "~9.4.5"
+    "@react-spring/shared" "~9.4.5"
+    "@react-spring/types" "~9.4.5"
+
+"@react-spring/types@~9.4.5":
+  version "9.4.5"
+  resolved "https://registry.yarnpkg.com/@react-spring/types/-/types-9.4.5.tgz#9c71e5ff866b5484a7ef3db822bf6c10e77bdd8c"
+  integrity sha512-mpRIamoHwql0ogxEUh9yr4TP0xU5CWyZxVQeccGkHHF8kPMErtDXJlxyo0lj+telRF35XNihtPTWoflqtyARmg==
+
+"@react-three/drei@^9.17.1":
+  version "9.17.1"
+  resolved "https://registry.yarnpkg.com/@react-three/drei/-/drei-9.17.1.tgz#5aaae55719750dca898464b924d705366c41ef96"
+  integrity sha512-1dSn+ulrs38D7jpEOjRMQmSLXhymBxw+KbBTxUtmOWDGi/T+T4wRxmy5lYJtv464yWrVOavCc3bgzOZDXJWAxA==
+  dependencies:
+    "@babel/runtime" "^7.11.2"
+    "@react-spring/three" "^9.3.1"
+    "@use-gesture/react" "^10.2.0"
+    detect-gpu "^4.0.19"
+    glsl-noise "^0.0.0"
+    lodash.omit "^4.5.0"
+    lodash.pick "^4.4.0"
+    meshline "^2.0.4"
+    react-composer "^5.0.3"
+    react-merge-refs "^1.1.0"
+    stats.js "^0.17.0"
+    suspend-react "^0.0.8"
+    three-mesh-bvh "^0.5.10"
+    three-stdlib "^2.10.2"
+    troika-three-text "^0.46.4"
+    utility-types "^3.10.0"
+    zustand "^3.5.13"
+
+"@react-three/fiber@^8.1.0":
+  version "8.1.0"
+  resolved "https://registry.yarnpkg.com/@react-three/fiber/-/fiber-8.1.0.tgz#b48a875aea126bd81d87a98c3893f7ff60da90ad"
+  integrity sha512-jtzwgmDq8Q4qi6/F6DETlRDdiTC5Mz/siIY1XoIk+xdw4gA6wqbH62enEKo4LpP+4o+hToGuGsKbARNA+x2tgQ==
+  dependencies:
+    "@babel/runtime" "^7.17.8"
+    "@types/react-reconciler" "^0.26.7"
+    react-reconciler "^0.27.0"
+    react-use-measure "^2.1.1"
+    scheduler "^0.21.0"
+    suspend-react "^0.0.8"
+    zustand "^3.7.1"
+
+"@react-three/postprocessing@^2.4.6":
+  version "2.4.6"
+  resolved "https://registry.yarnpkg.com/@react-three/postprocessing/-/postprocessing-2.4.6.tgz#1dfd6869ea064df36a7e4b770943c74a76a96377"
+  integrity sha512-LxCCtBamNT5uHN8Y1K5ec7ZqdmcIEoWUTWW0ujofX9kode5htk0E2zl8onSXMvRaDsOCMzP5EK3MycLXWgGFuA==
+  dependencies:
+    postprocessing "^6.28.1"
+    react-merge-refs "^1.1.0"
+    screen-space-reflections "^1.0.8"
+    three-stdlib "^2.8.11"
+
 "@rollup/plugin-babel@^5.2.0":
   version "5.3.1"
   resolved "https://registry.yarnpkg.com/@rollup/plugin-babel/-/plugin-babel-5.3.1.tgz#04bc0608f4aa4b2e4b1aebf284344d0f68fda283"
@@ -2010,6 +2124,11 @@
   resolved "https://registry.yarnpkg.com/@types/prettier/-/prettier-2.6.3.tgz#68ada76827b0010d0db071f739314fa429943d0a"
   integrity sha512-ymZk3LEC/fsut+/Q5qejp6R9O1rMxz3XaRHDV6kX8MrGAhOSPqVARbDi+EZvInBpw+BnCX3TD240byVkOfQsHg==
 
+"@types/prop-types@*":
+  version "15.7.5"
+  resolved "https://registry.yarnpkg.com/@types/prop-types/-/prop-types-15.7.5.tgz#5f19d2b85a98e9558036f6a3cacc8819420f05cf"
+  integrity sha512-JCB8C6SnDoQf0cNycqd/35A7MjcnK+ZTqE7judS6o7utxUCg6imJg3QK2qzHKszlTjcj2cn+NwMB2i96ubpj7w==
+
 "@types/q@^1.5.1":
   version "1.5.5"
   resolved "https://registry.yarnpkg.com/@types/q/-/q-1.5.5.tgz#75a2a8e7d8ab4b230414505d92335d1dcb53a6df"
@@ -2025,6 +2144,22 @@
   resolved "https://registry.yarnpkg.com/@types/range-parser/-/range-parser-1.2.4.tgz#cd667bcfdd025213aafb7ca5915a932590acdcdc"
   integrity sha512-EEhsLsD6UsDM1yFhAvy0Cjr6VwmpMWqFBCb9w07wVugF7w9nfajxLuVmngTIpgS6svCnm6Vaw+MZhoDCKnOfsw==
 
+"@types/react-reconciler@^0.26.7":
+  version "0.26.7"
+  resolved "https://registry.yarnpkg.com/@types/react-reconciler/-/react-reconciler-0.26.7.tgz#0c4643f30821ae057e401b0d9037e03e8e9b2a36"
+  integrity sha512-mBDYl8x+oyPX/VBb3E638N0B7xG+SPk/EAMcVPeexqus/5aTpTphQi0curhhshOqRrc9t6OPoJfEUkbymse/lQ==
+  dependencies:
+    "@types/react" "*"
+
+"@types/react@*":
+  version "18.0.15"
+  resolved "https://registry.yarnpkg.com/@types/react/-/react-18.0.15.tgz#d355644c26832dc27f3e6cbf0c4f4603fc4ab7fe"
+  integrity sha512-iz3BtLuIYH1uWdsv6wXYdhozhqj20oD4/Hk2DNXIn1kFsmp9x8d9QB6FnPhfkbhd2PgEONt9Q1x/ebkwjfFLow==
+  dependencies:
+    "@types/prop-types" "*"
+    "@types/scheduler" "*"
+    csstype "^3.0.2"
+
 "@types/resolve@1.17.1":
   version "1.17.1"
   resolved "https://registry.yarnpkg.com/@types/resolve/-/resolve-1.17.1.tgz#3afd6ad8967c77e4376c598a82ddd58f46ec45d6"
@@ -2037,6 +2172,11 @@
   resolved "https://registry.yarnpkg.com/@types/retry/-/retry-0.12.0.tgz#2b35eccfcee7d38cd72ad99232fbd58bffb3c84d"
   integrity sha512-wWKOClTTiizcZhXnPY4wikVAwmdYHp8q6DmC+EJUzAMsycb7HB32Kh9RN4+0gExjmPmZSAQjgURXIGATPegAvA==
 
+"@types/scheduler@*":
+  version "0.16.2"
+  resolved "https://registry.yarnpkg.com/@types/scheduler/-/scheduler-0.16.2.tgz#1a62f89525723dde24ba1b01b092bf5df8ad4d39"
+  integrity sha512-hppQEBDmlwhFAXKJX2KnWLYu5yMfi91yazPb2l+lbJiwW+wdo1gNeRA+3RgNSO39WYX2euey41KEwnqesU2Jew==
+
 "@types/serve-index@^1.9.1":
   version "1.9.1"
   resolved "https://registry.yarnpkg.com/@types/serve-index/-/serve-index-1.9.1.tgz#1b5e85370a192c01ec6cec4735cf2917337a6278"
@@ -2196,6 +2336,18 @@
     "@typescript-eslint/types" "5.30.5"
     eslint-visitor-keys "^3.3.0"
 
+"@use-gesture/core@10.2.16":
+  version "10.2.16"
+  resolved "https://registry.yarnpkg.com/@use-gesture/core/-/core-10.2.16.tgz#6c8db02955dca405b2544bf78f3ba1f094b6a551"
+  integrity sha512-2J2GICmrSA6RwcowqzL0TY6S8bJY35KhLnE0YjBIVobQL2RilSDefXTKMxxyontp2mALDeeYAHh9ZLu+vMaq8Q==
+
+"@use-gesture/react@^10.2.0":
+  version "10.2.16"
+  resolved "https://registry.yarnpkg.com/@use-gesture/react/-/react-10.2.16.tgz#467b4001c335905d35324ee147a2bb93b4f908e2"
+  integrity sha512-kkWi97SHzj/F6XqRXSyrk5pLoSiuRgqvnQl2Bawmf05dWo2q6DL7v5LhnnyPNZRVkCm+WEb3e1nsR+iVza1vmg==
+  dependencies:
+    "@use-gesture/core" "10.2.16"
+
 "@webassemblyjs/ast@1.11.1":
   version "1.11.1"
   resolved "https://registry.yarnpkg.com/@webassemblyjs/ast/-/ast-1.11.1.tgz#2bfd767eae1a6996f432ff7e8d7fc75679c0b6a7"
@@ -2317,6 +2469,11 @@
     "@webassemblyjs/ast" "1.11.1"
     "@xtuc/long" "4.2.2"
 
+"@webgpu/glslang@^0.0.15":
+  version "0.0.15"
+  resolved "https://registry.yarnpkg.com/@webgpu/glslang/-/glslang-0.0.15.tgz#f5ccaf6015241e6175f4b90906b053f88483d1f2"
+  integrity sha512-niT+Prh3Aff8Uf1MVBVUsaNjFj9rJAKDXuoHIKiQbB+6IUP/3J3JIhBNyZ7lDhytvXxw6ppgnwKZdDJ08UMj4Q==
+
 "@xtuc/ieee754@^1.2.0":
   version "1.2.0"
   resolved "https://registry.yarnpkg.com/@xtuc/ieee754/-/ieee754-1.2.0.tgz#eef014a3145ae477a1cbc00cd1e552336dceb790"
@@ -2831,6 +2988,13 @@ bfj@^7.0.2:
     hoopy "^0.1.4"
     tryer "^1.0.1"
 
+bidi-js@^1.0.2:
+  version "1.0.2"
+  resolved "https://registry.yarnpkg.com/bidi-js/-/bidi-js-1.0.2.tgz#1a497a762c2ddea377429d2649c9ce0f8a91527f"
+  integrity sha512-rzSy/k7WdX5zOyeHHCOixGXbCHkyogkxPKL2r8QtzHmVQDiWCXUWa18bLdMWT9CYMLOYTjWpTHawuev2ouYJVw==
+  dependencies:
+    require-from-string "^2.0.2"
+
 big.js@^5.2.2:
   version "5.2.2"
   resolved "https://registry.yarnpkg.com/big.js/-/big.js-5.2.2.tgz#65f0af382f578bcdc742bd9c281e9cb2d7768328"
@@ -3073,6 +3237,18 @@ check-types@^8.0.3:
   resolved "https://registry.yarnpkg.com/check-types/-/check-types-8.0.3.tgz#3356cca19c889544f2d7a95ed49ce508a0ecf552"
   integrity sha512-YpeKZngUmG65rLudJ4taU7VLkOCTMhNl/u4ctNC56LQS/zJTyNH0Lrtwm1tfTsbLlwvlfsA2d1c8vCf/Kh2KwQ==
 
+chevrotain@^10.1.2:
+  version "10.1.2"
+  resolved "https://registry.yarnpkg.com/chevrotain/-/chevrotain-10.1.2.tgz#c990ab43e32fd0bfb176ad1cbdebf48302ac8542"
+  integrity sha512-hvRiQuhhTZxkPMGD/dke+s1EGo8AkKDBU05CcufBO278qgAQSwIC4QyLdHz0CFHVtqVYWjlAS5D1KwvBbaHT+w==
+  dependencies:
+    "@chevrotain/cst-dts-gen" "^10.1.2"
+    "@chevrotain/gast" "^10.1.2"
+    "@chevrotain/types" "^10.1.2"
+    "@chevrotain/utils" "^10.1.2"
+    lodash "4.17.21"
+    regexp-to-ast "0.5.0"
+
 chokidar@^3.4.2, chokidar@^3.5.3:
   version "3.5.3"
   resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-3.5.3.tgz#1cf37c8707b932bd1af1ae22c0432e2acd1903bd"
@@ -3557,6 +3733,11 @@ cssstyle@^2.3.0:
   dependencies:
     cssom "~0.3.6"
 
+csstype@^3.0.2:
+  version "3.1.0"
+  resolved "https://registry.yarnpkg.com/csstype/-/csstype-3.1.0.tgz#4ddcac3718d787cf9df0d1b7d15033925c8f29f2"
+  integrity sha512-uX1KG+x9h5hIJsaKR9xHUeUraxf8IODOwq9JLNPq6BwB04a/xgpq3rcx47l5BZu5zBPlgD342tdke3Hom/nJRA==
+
 customize-cra@^1.0.0:
   version "1.0.0"
   resolved "https://registry.yarnpkg.com/customize-cra/-/customize-cra-1.0.0.tgz#73286563631aa08127ad4d30a2e3c89cf4e93c8d"
@@ -3578,10 +3759,10 @@ data-urls@^2.0.0:
     whatwg-mimetype "^2.3.0"
     whatwg-url "^8.0.0"
 
-debounce@^1.2.0:
-  version "1.2.0"
-  resolved "https://registry.yarnpkg.com/debounce/-/debounce-1.2.0.tgz#44a540abc0ea9943018dc0eaa95cce87f65cd131"
-  integrity sha512-mYtLl1xfZLi1m4RtQYlZgJUNQjl4ZxVnHzIR8nLLgi4q1YT8o/WM+MK/f8yfcc9s5Ir5zRaPZyZU6xs1Syoocg==
+debounce@^1.2.1:
+  version "1.2.1"
+  resolved "https://registry.yarnpkg.com/debounce/-/debounce-1.2.1.tgz#38881d8f4166a5c5848020c11827b834bcb3e0a5"
+  integrity sha512-XRRe6Glud4rd/ZGQfiV1ruXSfbvfJedlV9Y6zOlP+2K04vBYiJEte6stfFkCP03aMnY5tsipamumUjL14fofug==
 
 debug@2.6.9, debug@^2.6.0, debug@^2.6.9:
   version "2.6.9"
@@ -3684,6 +3865,13 @@ destroy@~1.0.4:
   resolved "https://registry.yarnpkg.com/destroy/-/destroy-1.0.4.tgz#978857442c44749e4206613e37946205826abd80"
   integrity sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA=
 
+detect-gpu@^4.0.19:
+  version "4.0.30"
+  resolved "https://registry.yarnpkg.com/detect-gpu/-/detect-gpu-4.0.30.tgz#682c4313d71490129e4c3eb2c6e1041b39277cc0"
+  integrity sha512-8LNqaFNkhNxeN74FXlXb3YRgcZEVgJzH1LxNDjCwuU1TdweQm1a4855Cupp6K3SNYW+KzT/2Bhr2z7HoQrTCwg==
+  dependencies:
+    webgl-constants "^1.1.1"
+
 detect-newline@^3.0.0:
   version "3.1.0"
   resolved "https://registry.yarnpkg.com/detect-newline/-/detect-newline-3.1.0.tgz#576f5dfc63ae1a192ff192d8ad3af6308991b651"
@@ -3852,20 +4040,10 @@ dotenv@^10.0.0:
   resolved "https://registry.yarnpkg.com/dotenv/-/dotenv-10.0.0.tgz#3d4227b8fb95f81096cdd2b66653fb2c7085ba81"
   integrity sha512-rlBi9d8jpv9Sf1klPjNfFAuWDjKLwTIJJ/VxtoTwIR6hnZxcEOQCZg2oIL3MWBYw5GpUDKOEnND7LXTbIpQ03Q==
 
-drei@^0.0.72:
-  version "0.0.72"
-  resolved "https://registry.yarnpkg.com/drei/-/drei-0.0.72.tgz#7ae9396371b9c5b358fdad3ec3d48fa42db510ca"
-  integrity sha512-rovaX6jr3Yghj5B4Aw+Do5JfR7gQLTTXdVID0oRt2tV4onmpPoIEVoI5GsO150qF6+JrNOstjpeQPiHnUcOC7A==
-  dependencies:
-    "@babel/runtime" "^7.10.5"
-    glsl-noise "^0.0.0"
-    lodash.omit "^4.5.0"
-    lodash.pick "^4.4.0"
-    postprocessing "^6.16.0"
-    react-merge-refs "^1.0.0"
-    stats.js "^0.17.0"
-    troika-three-text "^0.30.2"
-    utility-types "^3.10.0"
+draco3d@^1.4.1:
+  version "1.5.3"
+  resolved "https://registry.yarnpkg.com/draco3d/-/draco3d-1.5.3.tgz#75dfb3da7d1420571b1ab999191c49fdc2a74571"
+  integrity sha512-Ahum6SewAd1oVMm6Fk8T/zCE0qbzjohhO5pl1Xp5Outl4JKv7jYicfd5vNtkzImx94XE35fhNXVqHk9ajt+6Tg==
 
 duplexer@^0.1.1, duplexer@^0.1.2:
   version "0.1.2"
@@ -4415,11 +4593,6 @@ express@^4.17.3:
     utils-merge "1.0.1"
     vary "~1.1.2"
 
-fast-deep-equal@^2.0.1:
-  version "2.0.1"
-  resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-2.0.1.tgz#7b05218ddf9667bf7f370bf7fdb2cb15fdd0aa49"
-  integrity sha1-ewUhjd+WZ79/Nwv3/bLLFf3Qqkk=
-
 fast-deep-equal@^3.1.1, fast-deep-equal@^3.1.3:
   version "3.1.3"
   resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz#3a7d56b559d6cbc3eb512325244e619a65c6c525"
@@ -4467,6 +4640,11 @@ fb-watchman@^2.0.0:
   dependencies:
     bser "2.1.1"
 
+fflate@^0.6.9:
+  version "0.6.10"
+  resolved "https://registry.yarnpkg.com/fflate/-/fflate-0.6.10.tgz#5f40f9659205936a2d18abf88b2e7781662b6d43"
+  integrity sha512-IQrh3lEPM93wVCEczc9SaAOvkmcoQn/G8Bo1e8ZPlY3X3bnAxWaBdvTdvM1hP62iZp0BXWDy4vTAy4fF0+Dlpg==
+
 file-entry-cache@^6.0.1:
   version "6.0.1"
   resolved "https://registry.yarnpkg.com/file-entry-cache/-/file-entry-cache-6.0.1.tgz#211b2dd9659cb0394b073e7323ac3c933d522027"
@@ -6036,6 +6214,11 @@ klona@^2.0.4, klona@^2.0.5:
   resolved "https://registry.yarnpkg.com/klona/-/klona-2.0.5.tgz#d166574d90076395d9963aa7a928fabb8d76afbc"
   integrity sha512-pJiBpiXMbt7dkzXe8Ghj/u4FfXOOa98fPW+bihOJ4SjnoijweJrNThJfd3ifXpXhREjpoF2mZVH1GfS9LV3kHQ==
 
+ktx-parse@^0.2.1:
+  version "0.2.2"
+  resolved "https://registry.yarnpkg.com/ktx-parse/-/ktx-parse-0.2.2.tgz#b037b66044855215b332cb73104590af49e47791"
+  integrity sha512-cFBc1jnGG2WlUf52NbDUXK2obJ+Mo9WUkBRvr6tP6CKxRMvZwDDFNV3JAS4cewETp5KyexByfWm9sm+O8AffiQ==
+
 language-subtag-registry@~0.3.2:
   version "0.3.22"
   resolved "https://registry.yarnpkg.com/language-subtag-registry/-/language-subtag-registry-0.3.22.tgz#2e1500861b2e457eba7e7ae86877cbd08fa1fd1d"
@@ -6168,16 +6351,16 @@ lodash.uniq@^4.5.0:
   resolved "https://registry.yarnpkg.com/lodash.uniq/-/lodash.uniq-4.5.0.tgz#d0225373aeb652adc1bc82e4945339a842754773"
   integrity sha512-xfBaXQd9ryd9dlSDvnvI0lvxfLJlYAZzXomUYzLKtUeOQvOP5piqAWuGtrhWeqaXK9hhoM/iyJc5AV+XfsX3HQ==
 
+lodash@4.17.21, lodash@^4.17.20, lodash@^4.17.21, lodash@^4.7.0:
+  version "4.17.21"
+  resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c"
+  integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==
+
 lodash@^4.17.15, lodash@^4.17.19:
   version "4.17.20"
   resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.20.tgz#b44a9b6297bcb698f1c51a3545a2b3b368d59c52"
   integrity sha512-PlhdFcillOINfeV7Ni6oF1TAEayyZBoZ8bcshTHqOYJYlrqzRK5hagpagky5o4HfCzzd1TRkXPMFq6cKk9rGmA==
 
-lodash@^4.17.20, lodash@^4.17.21, lodash@^4.7.0:
-  version "4.17.21"
-  resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c"
-  integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==
-
 loose-envify@^1.1.0, loose-envify@^1.4.0:
   version "1.4.0"
   resolved "https://registry.yarnpkg.com/loose-envify/-/loose-envify-1.4.0.tgz#71ee51fa7be4caec1a63839f7e682d8132d30caf"
@@ -6257,6 +6440,11 @@ merge2@^1.3.0, merge2@^1.4.1:
   resolved "https://registry.yarnpkg.com/merge2/-/merge2-1.4.1.tgz#4368892f885e907455a6fd7dc55c0c9d404990ae"
   integrity sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==
 
+meshline@^2.0.4:
+  version "2.0.4"
+  resolved "https://registry.yarnpkg.com/meshline/-/meshline-2.0.4.tgz#39c7bcf36b503397642f2312e6211f2a8ecf75c5"
+  integrity sha512-Jh6DJl/zLqA4xsKvGv5950jr2ukyXQE1wgxs8u94cImHrvL6soVIggqjP+2hVHZXGYaKnWszhtjuCbKNeQyYiw==
+
 methods@~1.1.2:
   version "1.1.2"
   resolved "https://registry.yarnpkg.com/methods/-/methods-1.1.2.tgz#5529a4d67654134edcc5266656835b0f851afcee"
@@ -6366,6 +6554,11 @@ mkdirp@~0.5.1:
   dependencies:
     minimist "^1.2.6"
 
+mmd-parser@^1.0.4:
+  version "1.0.4"
+  resolved "https://registry.yarnpkg.com/mmd-parser/-/mmd-parser-1.0.4.tgz#87cc05782cb5974ca854f0303fc5147bc9d690e7"
+  integrity sha512-Qi0VCU46t2IwfGv5KF0+D/t9cizcDug7qnNoy9Ggk7aucp0tssV8IwTMkBlDbm+VqAf3cdQHTCARKSsuS2MYFg==
+
 ms@2.0.0:
   version "2.0.0"
   resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8"
@@ -6610,6 +6803,14 @@ opener@^1.5.1:
   resolved "https://registry.yarnpkg.com/opener/-/opener-1.5.2.tgz#5d37e1f35077b9dcac4301372271afdeb2a13598"
   integrity sha512-ur5UIdyw5Y7yEj9wLzhqXiy6GZ3Mwx0yGI+5sMn2r0N0v3cKJvUmFH5yPP+WXh9e0xfyzyJX95D8l088DNFj7A==
 
+opentype.js@^1.3.3:
+  version "1.3.4"
+  resolved "https://registry.yarnpkg.com/opentype.js/-/opentype.js-1.3.4.tgz#1c0e72e46288473cc4a4c6a2dc60fd7fe6020d77"
+  integrity sha512-d2JE9RP/6uagpQAVtJoF0pJJA/fgai89Cc50Yp0EJHk+eLp6QQ7gBoblsnubRULNY132I0J1QKMJ+JTbMqz4sw==
+  dependencies:
+    string.prototype.codepointat "^0.2.1"
+    tiny-inflate "^1.0.3"
+
 optionator@^0.8.1:
   version "0.8.3"
   resolved "https://registry.yarnpkg.com/optionator/-/optionator-0.8.3.tgz#84fa1d036fe9d3c7e21d99884b601167ec8fb495"
@@ -7376,10 +7577,15 @@ postcss@^8.3.5, postcss@^8.4.14, postcss@^8.4.4, postcss@^8.4.7:
     picocolors "^1.0.0"
     source-map-js "^1.0.2"
 
-postprocessing@^6.16.0, postprocessing@^6.17.1:
-  version "6.17.3"
-  resolved "https://registry.yarnpkg.com/postprocessing/-/postprocessing-6.17.3.tgz#78eff14a2e326245943e00e9d3ae02d2df203bc8"
-  integrity sha512-C7ZSFs6PrBx7/fkRbIJEpn/ris/1G6EYvWsr/Ipp24/KGHsLqHY8E+kWxYx2OFfvnvLCsN5kX5c8FxUjIPUSDQ==
+postprocessing@^6.28.1:
+  version "6.28.2"
+  resolved "https://registry.yarnpkg.com/postprocessing/-/postprocessing-6.28.2.tgz#7da068f0d54fbf37fed29b8e206ccf5eef8acd45"
+  integrity sha512-B2qBTiL3tXp5/rdWKG65ePBuQG6U/mie8K8mpvJrBfy9VhgQLJ5YOfx4Hjxv2vHSpXRs3rM0JwekficDFxCpyQ==
+
+potpack@^1.0.1:
+  version "1.0.2"
+  resolved "https://registry.yarnpkg.com/potpack/-/potpack-1.0.2.tgz#23b99e64eb74f5741ffe7656b5b5c4ddce8dfc14"
+  integrity sha512-choctRBIV9EMT9WGAZHn3V7t0Z2pMQyl0EZE6pFc/6ml3ssw7Dlf/oAOvFwjm1HVsqfQN8GfeFyJ+d8tRzqueQ==
 
 prelude-ls@^1.2.1:
   version "1.2.1"
@@ -7475,16 +7681,7 @@ prompts@^2.0.1, prompts@^2.4.2:
     kleur "^3.0.3"
     sisteransi "^1.0.5"
 
-prop-types@^15.6.2:
-  version "15.7.2"
-  resolved "https://registry.yarnpkg.com/prop-types/-/prop-types-15.7.2.tgz#52c41e75b8c87e72b9d9360e0206b99dcbffa6c5"
-  integrity sha512-8QQikdH7//R2vurIJSutZ1smHYTcLpRWEOlHnzcWHmBYrOGUysKwSsrC89BCiFj3CbrfJ/nXFdJepOVrY1GCHQ==
-  dependencies:
-    loose-envify "^1.4.0"
-    object-assign "^4.1.1"
-    react-is "^16.8.1"
-
-prop-types@^15.8.1:
+prop-types@^15.6.0, prop-types@^15.8.1:
   version "15.8.1"
   resolved "https://registry.yarnpkg.com/prop-types/-/prop-types-15.8.1.tgz#67d87bf1a694f48435cf332c24af10214a3140b5"
   integrity sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==
@@ -7604,6 +7801,13 @@ react-app-rewired@^2.2.1:
   dependencies:
     semver "^5.6.0"
 
+react-composer@^5.0.3:
+  version "5.0.3"
+  resolved "https://registry.yarnpkg.com/react-composer/-/react-composer-5.0.3.tgz#7beb9513da5e8687f4f434ea1333ef36a4f3091b"
+  integrity sha512-1uWd07EME6XZvMfapwZmc7NgCZqDemcvicRi3wMJzXsQLvZ3L7fTHVyPy1bZdnWXM4iPjYuNE+uJ41MLKeTtnA==
+  dependencies:
+    prop-types "^15.6.0"
+
 react-dev-utils@^12.0.1:
   version "12.0.1"
   resolved "https://registry.yarnpkg.com/react-dev-utils/-/react-dev-utils-12.0.1.tgz#ba92edb4a1f379bd46ccd6bcd4e7bc398df33e73"
@@ -7647,7 +7851,7 @@ react-error-overlay@^6.0.11:
   resolved "https://registry.yarnpkg.com/react-error-overlay/-/react-error-overlay-6.0.11.tgz#92835de5841c5cf08ba00ddd2d677b6d17ff9adb"
   integrity sha512-/6UZ2qgEyH2aqzYZgQPxEnz33NJ2gNsnHA2o5+o4wW9bLM/JYQitNP9xPhsXwC08hMMovfGe/8retsdDsczPRg==
 
-react-is@^16.12.0, react-is@^16.13.1, react-is@^16.8.1:
+react-is@^16.12.0, react-is@^16.13.1:
   version "16.13.1"
   resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.13.1.tgz#789729a4dc36de2999dc156dd6c1d9c18cea56a4"
   integrity sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==
@@ -7662,35 +7866,18 @@ react-is@^18.0.0:
   resolved "https://registry.yarnpkg.com/react-is/-/react-is-18.2.0.tgz#199431eeaaa2e09f86427efbb4f1473edb47609b"
   integrity sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==
 
-react-merge-refs@^1.0.0, react-merge-refs@^1.1.0:
+react-merge-refs@^1.1.0:
   version "1.1.0"
   resolved "https://registry.yarnpkg.com/react-merge-refs/-/react-merge-refs-1.1.0.tgz#73d88b892c6c68cbb7a66e0800faa374f4c38b06"
   integrity sha512-alTKsjEL0dKH/ru1Iyn7vliS2QRcBp9zZPGoWxUOvRGWPUYgjo+V01is7p04It6KhgrzhJGnIj9GgX8W4bZoCQ==
 
-react-postprocessing@^1.1.4:
-  version "1.4.0"
-  resolved "https://registry.yarnpkg.com/react-postprocessing/-/react-postprocessing-1.4.0.tgz#caba52f4f6efc839bfbdeed5414f4dd288db0fa4"
-  integrity sha512-IcUfFSGigmZ9E8u07A6WgB9uVYpwiFL8Tvycadcbu0vzI0bbqthikoNKD5zDWv0h5mnUu/y95kIDR5AViup0Fw==
-  dependencies:
-    postprocessing "^6.17.1"
-    react-merge-refs "^1.1.0"
-
-react-promise-suspense@^0.3.2:
-  version "0.3.3"
-  resolved "https://registry.yarnpkg.com/react-promise-suspense/-/react-promise-suspense-0.3.3.tgz#b085c7e0ac22b85fd3d605b1c4f181cda4310bc9"
-  integrity sha512-OdehKsCEWYoV6pMcwxbvJH99UrbXylmXJ1QpEL9OfHaUBzcAihyfSJV8jFq325M/wW9iKc/BoiLROXxMul+MxA==
-  dependencies:
-    fast-deep-equal "^2.0.1"
-
-react-reconciler@0.25.1:
-  version "0.25.1"
-  resolved "https://registry.yarnpkg.com/react-reconciler/-/react-reconciler-0.25.1.tgz#f9814d59d115e1210762287ce987801529363aaa"
-  integrity sha512-R5UwsIvRcSs3w8n9k3tBoTtUHdVhu9u84EG7E5M0Jk9F5i6DA1pQzPfUZd6opYWGy56MJOtV3VADzy6DRwYDjw==
+react-reconciler@^0.27.0:
+  version "0.27.0"
+  resolved "https://registry.yarnpkg.com/react-reconciler/-/react-reconciler-0.27.0.tgz#360124fdf2d76447c7491ee5f0e04503ed9acf5b"
+  integrity sha512-HmMDKciQjYmBRGuuhIaKA1ba/7a+UsM5FzOZsMO2JYHt9Jh8reCb7j1eDC95NOyUlKM9KRyvdx0flBuDvYSBoA==
   dependencies:
     loose-envify "^1.1.0"
-    object-assign "^4.1.1"
-    prop-types "^15.6.2"
-    scheduler "^0.19.1"
+    scheduler "^0.21.0"
 
 react-refresh@^0.11.0:
   version "0.11.0"
@@ -7752,28 +7939,12 @@ react-scripts@^5.0.1:
   optionalDependencies:
     fsevents "^2.3.2"
 
-react-three-fiber@^4.2.20:
-  version "4.2.21"
-  resolved "https://registry.yarnpkg.com/react-three-fiber/-/react-three-fiber-4.2.21.tgz#bb335fee090a44f2ba48762a8c42308c31f4238f"
-  integrity sha512-lbopEkL36cbAaG/y+iEGT1EFbVaVZBrOe2XGt2+HxsCL8AeWWiQQERo1HYiiqFc4p6DuoNq1hhOSxr1TKQjXuQ==
-  dependencies:
-    "@babel/runtime" "^7.9.2"
-    "@juggle/resize-observer" "^3.1.3"
-    react-merge-refs "^1.0.0"
-    react-promise-suspense "^0.3.2"
-    react-reconciler "0.25.1"
-    react-use-measure "^2.0.0"
-    resize-observer-polyfill "^1.5.1"
-    scheduler "0.19.1"
-    tiny-emitter "^2.1.0"
-    utility-types "^3.10.0"
-
-react-use-measure@^2.0.0:
-  version "2.0.1"
-  resolved "https://registry.yarnpkg.com/react-use-measure/-/react-use-measure-2.0.1.tgz#4f23f94c832cd4512da55acb300d1915dcbf3ae8"
-  integrity sha512-lFfHiqcXbJ2/6aUkZwt8g5YYM7EGqNVxJhMqMPqv1BVXRKp8D7jYLlmma0SvhRY4WYxxkZpCdbJvhDylb5gcEA==
+react-use-measure@^2.1.1:
+  version "2.1.1"
+  resolved "https://registry.yarnpkg.com/react-use-measure/-/react-use-measure-2.1.1.tgz#5824537f4ee01c9469c45d5f7a8446177c6cc4ba"
+  integrity sha512-nocZhN26cproIiIduswYpV5y5lQpSQS1y/4KuvUCjSKmw7ZWIS/+g3aFnX3WdBkyuGUtTLif3UTqnLLhbDoQig==
   dependencies:
-    debounce "^1.2.0"
+    debounce "^1.2.1"
 
 react@^18.2.0:
   version "18.2.0"
@@ -7867,6 +8038,11 @@ regex-parser@^2.2.11:
   resolved "https://registry.yarnpkg.com/regex-parser/-/regex-parser-2.2.11.tgz#3b37ec9049e19479806e878cabe7c1ca83ccfe58"
   integrity sha512-jbD/FT0+9MBU2XAZluI7w2OBs1RBi6p9M83nkoZayQXXU9e8Robt69FcZc7wU4eJD/YFTjn1JdCk3rbMJajz8Q==
 
+regexp-to-ast@0.5.0:
+  version "0.5.0"
+  resolved "https://registry.yarnpkg.com/regexp-to-ast/-/regexp-to-ast-0.5.0.tgz#56c73856bee5e1fef7f73a00f1473452ab712a24"
+  integrity sha512-tlbJqcMHnPKI9zSrystikWKwHkBqu2a/Sgw01h3zFjvYrMxEDYHzzoMZnUrbIfpTFEsoRnnviOXNCzFiSc54Qw==
+
 regexp.prototype.flags@^1.4.1, regexp.prototype.flags@^1.4.3:
   version "1.4.3"
   resolved "https://registry.yarnpkg.com/regexp.prototype.flags/-/regexp.prototype.flags-1.4.3.tgz#87cab30f80f66660181a3bb7bf5981a872b367ac"
@@ -7936,11 +8112,6 @@ requires-port@^1.0.0:
   resolved "https://registry.yarnpkg.com/requires-port/-/requires-port-1.0.0.tgz#925d2601d39ac485e091cf0da5c6e694dc3dcaff"
   integrity sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==
 
-resize-observer-polyfill@^1.5.1:
-  version "1.5.1"
-  resolved "https://registry.yarnpkg.com/resize-observer-polyfill/-/resize-observer-polyfill-1.5.1.tgz#0e9020dd3d21024458d4ebd27e23e40269810464"
-  integrity sha512-LwZrotdHOo12nQuZlHEmtuXdqGoOD0OhaxopaNFxWzInpEgaLWoVuAMbTzixuosCx2nEG58ngzW3vxdWoxIgdg==
-
 resolve-cwd@^3.0.0:
   version "3.0.0"
   resolved "https://registry.yarnpkg.com/resolve-cwd/-/resolve-cwd-3.0.0.tgz#0f0075f1bb2544766cf73ba6a6e2adfebcb13f2d"
@@ -8073,13 +8244,12 @@ saxes@^5.0.1:
   dependencies:
     xmlchars "^2.2.0"
 
-scheduler@0.19.1, scheduler@^0.19.1:
-  version "0.19.1"
-  resolved "https://registry.yarnpkg.com/scheduler/-/scheduler-0.19.1.tgz#4f3e2ed2c1a7d65681f4c854fa8c5a1ccb40f196"
-  integrity sha512-n/zwRWRYSUj0/3g/otKDRPMh6qv2SYMWNq85IEa8iZyAv8od9zDYpGSnpBEjNgcMNq6Scbu5KfIPxNF72R/2EA==
+scheduler@^0.21.0:
+  version "0.21.0"
+  resolved "https://registry.yarnpkg.com/scheduler/-/scheduler-0.21.0.tgz#6fd2532ff5a6d877b6edb12f00d8ab7e8f308820"
+  integrity sha512-1r87x5fz9MXqswA2ERLo0EbOAU74DpIUO090gIasYTqlVoJeMcl+Z1Rg7WHz+qtPujhS/hGIt9kxZOYBV3faRQ==
   dependencies:
     loose-envify "^1.1.0"
-    object-assign "^4.1.1"
 
 scheduler@^0.23.0:
   version "0.23.0"
@@ -8125,6 +8295,11 @@ schema-utils@^4.0.0:
     ajv-formats "^2.1.1"
     ajv-keywords "^5.0.0"
 
+screen-space-reflections@^1.0.8:
+  version "1.0.8"
+  resolved "https://registry.yarnpkg.com/screen-space-reflections/-/screen-space-reflections-1.0.8.tgz#304de62086cdcfa92151a48d19a608539e548586"
+  integrity sha512-UBahDIY3TBds7RIkiGmBMQLZKjBYuAdatOBHEs8jpkSe414cJ66nKNPh/cBFL7ZekTorluNbmxdbJ1GEw/e8xQ==
+
 select-hose@^2.0.0:
   version "2.0.0"
   resolved "https://registry.yarnpkg.com/select-hose/-/select-hose-2.0.0.tgz#625d8658f865af43ec962bfc376a37359a4994ca"
@@ -8466,6 +8641,11 @@ string-width@^4.1.0, string-width@^4.2.0:
     is-fullwidth-code-point "^3.0.0"
     strip-ansi "^6.0.1"
 
+string.prototype.codepointat@^0.2.1:
+  version "0.2.1"
+  resolved "https://registry.yarnpkg.com/string.prototype.codepointat/-/string.prototype.codepointat-0.2.1.tgz#004ad44c8afc727527b108cd462b4d971cd469bc"
+  integrity sha512-2cBVCj6I4IOvEnjgO/hWqXjqBGsY+zwPmHl12Srk9IXSZ56Jwwmy+66XO5Iut/oQVR7t5ihYdLB0GMa4alEUcg==
+
 string.prototype.matchall@^4.0.6, string.prototype.matchall@^4.0.7:
   version "4.0.7"
   resolved "https://registry.yarnpkg.com/string.prototype.matchall/-/string.prototype.matchall-4.0.7.tgz#8e6ecb0d8a1fb1fda470d81acecb2dba057a481d"
@@ -8614,6 +8794,11 @@ supports-preserve-symlinks-flag@^1.0.0:
   resolved "https://registry.yarnpkg.com/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz#6eda4bd344a3c94aea376d4cc31bc77311039e09"
   integrity sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==
 
+suspend-react@^0.0.8:
+  version "0.0.8"
+  resolved "https://registry.yarnpkg.com/suspend-react/-/suspend-react-0.0.8.tgz#b0740c1386b4eb652f17affe4339915ee268bd31"
+  integrity sha512-ZC3r8Hu1y0dIThzsGw0RLZplnX9yXwfItcvaIzJc2VQVi8TGyGDlu92syMB5ulybfvGLHAI5Ghzlk23UBPF8xg==
+
 svg-parser@^2.0.2:
   version "2.0.4"
   resolved "https://registry.yarnpkg.com/svg-parser/-/svg-parser-2.0.4.tgz#fdc2e29e13951736140b76cb122c8ee6630eb6b5"
@@ -8752,15 +8937,31 @@ text-table@^0.2.0:
   resolved "https://registry.yarnpkg.com/text-table/-/text-table-0.2.0.tgz#7f5ee823ae805207c00af2df4a84ec3fcfa570b4"
   integrity sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==
 
-three@^0.120.1:
-  version "0.120.1"
-  resolved "https://registry.yarnpkg.com/three/-/three-0.120.1.tgz#dbd8894f8ab87c109f1602933e7c740c98137377"
-  integrity sha512-ktaCRFUR7JUZcKec+cBRz+oBex5pOVaJhrtxvFF2T7on53o9UkEux+/Nh1g/4zeb4t/pbxIFcADbn/ACu3LC1g==
-
-threejs-meshline@^2.0.11:
-  version "2.0.12"
-  resolved "https://registry.yarnpkg.com/threejs-meshline/-/threejs-meshline-2.0.12.tgz#503e0cabb4b8572f1f25882997577a072cfa983d"
-  integrity sha512-2U3SyGWAotT/TJCO9NhBDiaAPUq8X+bgh7hK7fDlwoBg9yOPvjaVzIf8b0A60k7poNhEg225qWaX7WeYlUjC7g==
+three-mesh-bvh@^0.5.10:
+  version "0.5.14"
+  resolved "https://registry.yarnpkg.com/three-mesh-bvh/-/three-mesh-bvh-0.5.14.tgz#605654cf802b3e92ea7b18997535960d04344159"
+  integrity sha512-rSk7SpKDR47m1PmkUxt5na5+dM4h0KW5zF43lMniZGjT4hQn9Om+g64ooFreFIWlQlrNN8pFqiPnDMRZS58/Yw==
+
+three-stdlib@^2.10.2, three-stdlib@^2.8.11:
+  version "2.12.1"
+  resolved "https://registry.yarnpkg.com/three-stdlib/-/three-stdlib-2.12.1.tgz#b63de05cf7f7803e630a7c0c5887e85ae06178ca"
+  integrity sha512-G3SSsCOBiWa0sjjPt+K28ikQ84Plm/ZVUozMfWagK59kZqBWcaPVXpOThkAgvdBpm2zCWLW3edAoW/4XIbljVQ==
+  dependencies:
+    "@babel/runtime" "^7.16.7"
+    "@webgpu/glslang" "^0.0.15"
+    chevrotain "^10.1.2"
+    draco3d "^1.4.1"
+    fflate "^0.6.9"
+    ktx-parse "^0.2.1"
+    mmd-parser "^1.0.4"
+    opentype.js "^1.3.3"
+    potpack "^1.0.1"
+    zstddec "^0.0.2"
+
+three@^0.141.0:
+  version "0.141.0"
+  resolved "https://registry.yarnpkg.com/three/-/three-0.141.0.tgz#16677a12b9dd0c3e1568ebad0fd09de15d5a8216"
+  integrity sha512-JaSDAPWuk4RTzG5BYRQm8YZbERUxTfTDVouWgHMisS2to4E5fotMS9F2zPFNOIJyEFTTQDDKPpsgZVThKU3pXA==
 
 throat@^6.0.1:
   version "6.0.1"
@@ -8772,11 +8973,16 @@ thunky@^1.0.2:
   resolved "https://registry.yarnpkg.com/thunky/-/thunky-1.1.0.tgz#5abaf714a9405db0504732bbccd2cedd9ef9537d"
   integrity sha512-eHY7nBftgThBqOyHGVN+l8gF0BucP09fMo0oO/Lb0w1OF80dJv+lDVpXG60WMQvkcxAkNybKsrEIE3ZtKGmPrA==
 
-tiny-emitter@^2.0.0, tiny-emitter@^2.1.0:
+tiny-emitter@^2.0.0:
   version "2.1.0"
   resolved "https://registry.yarnpkg.com/tiny-emitter/-/tiny-emitter-2.1.0.tgz#1d1a56edfc51c43e863cbb5382a72330e3555423"
   integrity sha512-NB6Dk1A9xgQPMoGqC5CVXn123gWyte215ONT5Pp5a0yt4nlEoO1ZWeCwpncaekPHXO60i47ihFnZPiRPjRMq4Q==
 
+tiny-inflate@^1.0.3:
+  version "1.0.3"
+  resolved "https://registry.yarnpkg.com/tiny-inflate/-/tiny-inflate-1.0.3.tgz#122715494913a1805166aaf7c93467933eea26c4"
+  integrity sha512-pkY1fj1cKHb2seWDy0B16HeWyczlJA9/WW3u3c4z/NiWDsO3DOU5D7nhTLE9CF0yXv/QZFY7sEJmj24dK+Rrqw==
+
 tmpl@1.0.5:
   version "1.0.5"
   resolved "https://registry.yarnpkg.com/tmpl/-/tmpl-1.0.5.tgz#8683e0b902bb9c20c4f726e3c0b69f36518c07cc"
@@ -8827,23 +9033,25 @@ tr46@^2.1.0:
   dependencies:
     punycode "^2.1.1"
 
-troika-three-text@^0.30.2:
-  version "0.30.2"
-  resolved "https://registry.yarnpkg.com/troika-three-text/-/troika-three-text-0.30.2.tgz#ed82c57c2a06c5f87ca38087ebd54c72560fc786"
-  integrity sha512-FEOmjIm3yEO/Vlg5oe3cvAN3YWW55TRWeDRen3F2Xlo8RTx0Icsy3AZRrwd9ycT2FbuNzHVjnwxAVXX5F+B21A==
+troika-three-text@^0.46.4:
+  version "0.46.4"
+  resolved "https://registry.yarnpkg.com/troika-three-text/-/troika-three-text-0.46.4.tgz#77627ac2ac4765d5248c857a8b42f82c25f2d034"
+  integrity sha512-Qsv0HhUKTZgSmAJs5wvO7YlBoJSP9TGPLmrg+K9pbQq4lseQdcevbno/WI38bwJBZ/qS56hvfqEzY0zUEFzDIw==
   dependencies:
-    troika-three-utils "^0.30.0"
-    troika-worker-utils "^0.30.0"
+    bidi-js "^1.0.2"
+    troika-three-utils "^0.46.0"
+    troika-worker-utils "^0.46.0"
+    webgl-sdf-generator "1.1.1"
 
-troika-three-utils@^0.30.0:
-  version "0.30.0"
-  resolved "https://registry.yarnpkg.com/troika-three-utils/-/troika-three-utils-0.30.0.tgz#5e118ee68afbe63b31c3e7bf4d509499e036a64f"
-  integrity sha512-ZXTLb3tSCxbJEQyz4hwxYOx/Hrg9rzTrDSqGCmUTRXyOMOtZxrFeHTLBh2kblIb7JfhMUofj9vtRfIjt1UYYQA==
+troika-three-utils@^0.46.0:
+  version "0.46.0"
+  resolved "https://registry.yarnpkg.com/troika-three-utils/-/troika-three-utils-0.46.0.tgz#6d97a9bf08f2260285edf2bb0be6328dd3d50eec"
+  integrity sha512-llHyrXAcwzr0bpg80GxsIp73N7FuImm4WCrKDJkAqcAsWmE5pfP9+Qzw+oMWK1P/AdHQ79eOrOl9NjyW4aOw0w==
 
-troika-worker-utils@^0.30.0:
-  version "0.30.0"
-  resolved "https://registry.yarnpkg.com/troika-worker-utils/-/troika-worker-utils-0.30.0.tgz#4b244d5939512a39459ede38b70b2ab9933270a5"
-  integrity sha512-GpW0LK+KCdZQclP50ZdFlVviImeiqEC4I5CQF+sQurcOb9xC6cL5FyrdV1C4abtQ67G8ir3DA8+HktYi35whdg==
+troika-worker-utils@^0.46.0:
+  version "0.46.0"
+  resolved "https://registry.yarnpkg.com/troika-worker-utils/-/troika-worker-utils-0.46.0.tgz#1b698090af78b51a27e03881c90237a2e648d6c4"
+  integrity sha512-bzOx5f2ZBxkFhXtIvDJlLn2AI3bzCkGVbCndl/2dL5QZrwHEKl45OEIilCxYQQWJG1rEbOD9O80tMjoYjw19OA==
 
 tryer@^1.0.1:
   version "1.0.1"
@@ -9096,6 +9304,16 @@ wbuf@^1.1.0, wbuf@^1.7.3:
   dependencies:
     minimalistic-assert "^1.0.0"
 
+webgl-constants@^1.1.1:
+  version "1.1.1"
+  resolved "https://registry.yarnpkg.com/webgl-constants/-/webgl-constants-1.1.1.tgz#f9633ee87fea56647a60b9ce735cbdfb891c6855"
+  integrity sha512-LkBXKjU5r9vAW7Gcu3T5u+5cvSvh5WwINdr0C+9jpzVB41cjQAP5ePArDtk/WHYdVj0GefCgM73BA7FlIiNtdg==
+
+webgl-sdf-generator@1.1.1:
+  version "1.1.1"
+  resolved "https://registry.yarnpkg.com/webgl-sdf-generator/-/webgl-sdf-generator-1.1.1.tgz#3e1b422b3d87cd3cc77f2602c9db63bc0f6accbd"
+  integrity sha512-9Z0JcMTFxeE+b2x1LJTdnaT8rT8aEp7MVxkNwoycNmJWwPdzoXzMh0BjJSh/AEFP+KPYZUli814h8bJZFIZ2jA==
+
 webidl-conversions@^4.0.2:
   version "4.0.2"
   resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-4.0.2.tgz#a855980b1f0b6b359ba1d5d9fb39ae941faa63ad"
@@ -9582,7 +9800,17 @@ yocto-queue@^0.1.0:
   resolved "https://registry.yarnpkg.com/yocto-queue/-/yocto-queue-0.1.0.tgz#0294eb3dee05028d31ee1a5fa2c556a6aaf10a1b"
   integrity sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==
 
+zstddec@^0.0.2:
+  version "0.0.2"
+  resolved "https://registry.yarnpkg.com/zstddec/-/zstddec-0.0.2.tgz#57e2f28dd1ff56b750e07d158a43f0611ad9eeb4"
+  integrity sha512-DCo0oxvcvOTGP/f5FA6tz2Z6wF+FIcEApSTu0zV5sQgn9hoT5lZ9YRAKUraxt9oP7l4e8TnNdi8IZTCX6WCkwA==
+
 zustand@^3.0.0:
   version "3.1.2"
   resolved "https://registry.yarnpkg.com/zustand/-/zustand-3.1.2.tgz#0dcb9197e38d82746ae2a22097d80009cd0efa4f"
   integrity sha512-b9pbR29m/KYicTE33dF+SPQ3DoDnMT4ZHOS00QnzZ/SWArK7rn/3EVeSWWk3G2A9bexEHqj75Ob2w+GN547o/w==
+
+zustand@^3.5.13, zustand@^3.7.1:
+  version "3.7.2"
+  resolved "https://registry.yarnpkg.com/zustand/-/zustand-3.7.2.tgz#7b44c4f4a5bfd7a8296a3957b13e1c346f42514d"
+  integrity sha512-PIJDIZKtokhof+9+60cpockVOq05sJzHCriyvaLBmEJixseQ1a5Kdov6fWZfWOu5SK9c+FhH1jU0tntLxRJYMA==
diff --git a/src/middleware/persist.ts b/src/middleware/persist.ts
index 861eb0c..d81e629 100644
--- a/src/middleware/persist.ts
+++ b/src/middleware/persist.ts
@@ -85,6 +85,7 @@ interface StorePersist<S extends State, Ps> {
     hasHydrated: () => boolean
     onHydrate: (fn: PersistListener<S>) => () => void
     onFinishHydration: (fn: PersistListener<S>) => () => void
+    getOptions: () => Partial<PersistOptions<S, Ps>>
   }
 }
 
@@ -276,6 +277,7 @@ const persistImpl: PersistImpl = (config, baseOptions) => (set, get, api) => {
     clearStorage: () => {
       storage?.removeItem(options.name)
     },
+    getOptions: () => options,
     rehydrate: () => hydrate() as Promise<void>,
     hasHydrated: () => hasHydrated,
     onHydrate: (cb) => {
@@ -303,7 +305,7 @@ type Persist = <
   T extends State,
   Mps extends [StoreMutatorIdentifier, unknown][] = [],
   Mcs extends [StoreMutatorIdentifier, unknown][] = [],
-  U = Partial<T>
+  U = T
 >(
   initializer: StateCreator<T, [...Mps, ['zustand/persist', unknown]], Mcs>,
   options?: PersistOptions<T, U>
diff --git a/src/shallow.ts b/src/shallow.ts
index d585373..1aaf2a5 100644
--- a/src/shallow.ts
+++ b/src/shallow.ts
@@ -1,4 +1,24 @@
-export default function shallow<T, U>(objA: T, objB: U) {
+function shallow<
+  T extends (...args: unknown[]) => unknown,
+  U extends (...args: unknown[]) => unknown
+>(objA: T, objB: U): boolean
+
+function shallow<
+  T extends string | number | boolean,
+  U extends string | number | boolean
+>(objA: T, objB: U): boolean
+
+function shallow<T extends unknown[], U extends unknown[]>(
+  objA: T,
+  objB: U
+): boolean
+
+function shallow<
+  T extends Record<string, unknown>,
+  U extends Record<string, unknown>
+>(objA: T, objB: U): boolean
+
+function shallow<T, U>(objA: T, objB: U) {
   if (Object.is(objA, objB)) {
     return true
   }
@@ -24,3 +44,5 @@ export default function shallow<T, U>(objA: T, objB: U) {
   }
   return true
 }
+
+export default shallow
diff --git a/src/vanilla.ts b/src/vanilla.ts
index f28a034..0ccfc14 100644
--- a/src/vanilla.ts
+++ b/src/vanilla.ts
@@ -15,7 +15,7 @@ export interface Subscribe<T extends State> {
 
 export type SetState<T extends State> = {
   _(
-    partial: T | Partial<T> | ((state: T) => T | Partial<T>),
+    partial: T | Partial<T> | { _(state: T): T | Partial<T> }['_'],
     replace?: boolean | undefined
   ): void
 }['_']


Test patch:
diff --git a/tests/persistSync.test.tsx b/tests/persistSync.test.tsx
index 798c987..aebe09e 100644
--- a/tests/persistSync.test.tsx
+++ b/tests/persistSync.test.tsx
@@ -381,6 +381,23 @@ describe('persist middleware with sync configuration', () => {
     )
   })
 
+  it('can access the options through the api', () => {
+    const storage = {
+      getItem: () => null,
+      setItem: jest.fn(),
+      removeItem: () => {},
+    }
+
+    const useStore = create(
+      persist(() => ({ count: 0 }), {
+        name: 'test-storage',
+        getStorage: () => storage,
+      })
+    )
+    expect(useStore.persist.getOptions().name).toBeDefined()
+    expect(useStore.persist.getOptions().name).toBe('test-storage')
+  })
+
   it('can change the options through the api', () => {
     const setItemSpy = jest.fn()
 
@@ -394,6 +411,7 @@ describe('persist middleware with sync configuration', () => {
       persist(() => ({ count: 0 }), {
         name: 'test-storage',
         getStorage: () => storage,
+        partialize: (s) => s as Partial<typeof s>,
       })
     )