diff --git a/README.md b/README.md index 3da77e5..376c890 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,57 @@ # use-travel + A React hook for state time travel with undo, redo, and reset functionalities. -TODO +### Installation + +```bash +npm install use-travel mutative +# or +yarn add use-travel mutative +``` + +### Features + +- Undo/Redo/Reset +- Small size for time travel with Patches history +- Customizable history size +- Customizable initial patches +- High performance +- Mark function for custom immutability + +### TODO + +- [ ] add `archive` functionality + +### API + +```jsx +import { useTravel } from 'use-travel'; + +const App = () => { + const [state, setState, controls]} = useTravel(0, { + maxHistory: 10, + initialPatches: [], + }); + return ( +
+
{state}
+ + + + + + {controls.getHistory().map((state, index) => ( +
{state}
+ ))} + {controls.patches.map((patch, index) => ( +
{patch}
+ ))} +
{controls.position}
+ +
+ ); +} +``` diff --git a/package.json b/package.json index d73417b..1d16829 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "use-travel", - "version": "0.0.1", + "version": "0.1.0", "description": "A React hook for state time travel with undo, redo, and reset functionalities.", "main": "dist/index.cjs.js", "unpkg": "dist/index.umd.js", @@ -43,8 +43,10 @@ "@rollup/plugin-commonjs": "^24.0.1", "@rollup/plugin-node-resolve": "^15.0.1", "@rollup/plugin-replace": "^5.0.2", + "@testing-library/react": "^14.2.1", "@types/jest": "^29.5.0", "@types/node": "^18.15.5", + "@types/react": "^18.2.66", "@typescript-eslint/eslint-plugin": "^5.56.0", "@typescript-eslint/parser": "^5.56.0", "commitizen": "^4.3.0", @@ -57,6 +59,8 @@ "jest-environment-jsdom": "^29.5.0", "mutative": "^1.0.3", "prettier": "^2.8.6", + "react": "^18.2.0", + "react-dom": "^18.2.0", "rimraf": "^4.4.0", "rollup": "^3.20.0", "rollup-plugin-terser": "^7.0.0", @@ -81,6 +85,11 @@ } }, "peerDependencies": { - "mutative": "^1.0.3" + "@types/react": "^18.0 || ^17.0", + "mutative": "^1.0.3", + "react": "^18.0 || ^17.0" + }, + "dependencies": { + "use-mutative": "^1.0.0" } } diff --git a/src/index.ts b/src/index.ts index a613b6b..cee2afe 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,3 +1,209 @@ -export const useTravel = (state: any) => { - // +import { useCallback, useEffect, useMemo } from 'react'; +import { + type Options as MutativeOptions, + type Patches, + type Draft, + type Immutable, + apply, + rawReturn, +} from 'mutative'; +import { useMutative } from 'use-mutative'; + +type TravelPatches = { + patches: Patches[]; + inversePatches: Patches[]; +}; + +type Options = { + maxHistory?: number; + initialPatches?: TravelPatches; + autoArchive?: A; +} & MutativeOptions; + +type InitialValue = I extends (...args: any) => infer R ? R : I; +type DraftFunction = (draft: Draft) => void; +type Updater = (value: S | (() => S) | DraftFunction) => void; +type Value = F extends true + ? Immutable> + : InitialValue; +type StateValue = + | InitialValue + | (() => InitialValue) + | DraftFunction>; + +type Result = [ + Value, + Updater>, + { + /** + * The current position in the history + */ + position: number; + /** + * Get the history of the state + */ + getHistory: () => Value[]; + /** + * The patches of the history + */ + patches: TravelPatches; + /** + * Go back in the history + */ + back: (amount?: number) => void; + /** + * Go forward in the history + */ + forward: (amount?: number) => void; + /** + * Reset the history + */ + reset: () => void; + /** + * Go to a specific position in the history + */ + go: (position: number) => void; + /** + * Check if it's possible to go back + */ + canBack: () => boolean; + /** + * Check if it's possible to go forward + */ + canForward: () => boolean; + } +]; + +/** + * A hook to travel in the history of a state + */ +export const useTravel = ( + initialState: S, + { maxHistory = 10, initialPatches, ...options }: Options = {} +) => { + const [position, setPosition] = useMutative(-1); + const [allPatches, setAllPatches] = useMutative( + () => + (initialPatches ?? { + patches: [], + inversePatches: [], + }) as TravelPatches + ); + const [state, setState, patches, inversePatches] = useMutative(initialState, { + ...options, + enablePatches: true, + }); + useEffect(() => { + if (position === -1 && patches.length > 0) { + setAllPatches((_allPatches) => { + _allPatches.patches.push(patches); + _allPatches.inversePatches.push(inversePatches); + if (maxHistory < _allPatches.patches.length) { + _allPatches.patches = _allPatches.patches.slice(-maxHistory); + _allPatches.inversePatches = _allPatches.inversePatches.slice( + -maxHistory + ); + } + }); + } + }, [maxHistory, patches, inversePatches, position]); + const cachedPosition = useMemo( + () => (position === -1 ? allPatches.patches.length : position), + [position, allPatches.patches.length] + ); + const cachedTravels = useMemo(() => { + const go = (nextPosition: number) => { + const back = nextPosition < cachedPosition; + if (nextPosition > allPatches.patches.length) { + console.warn(`Can't go forward to position ${nextPosition}`); + nextPosition = allPatches.patches.length; + } + if (nextPosition < 0) { + console.warn(`Can't go back to position ${nextPosition}`); + nextPosition = 0; + } + if (nextPosition === cachedPosition) return; + setPosition(nextPosition); + setState(() => + rawReturn( + apply( + state as object, + back + ? allPatches.inversePatches.slice(nextPosition).flat().reverse() + : allPatches.patches.slice(position, nextPosition).flat() + ) + ) + ); + }; + return { + position: cachedPosition, + getHistory: () => { + const history = [state]; + let currentState = state as any; + for (let i = cachedPosition; i < allPatches.patches.length; i++) { + currentState = apply( + currentState as object, + allPatches.patches[i] + ) as S; + history.push(currentState); + } + currentState = state as any; + const inversePatches = allPatches.inversePatches; + const stateIndex = + inversePatches.length === cachedPosition + ? inversePatches.length - 1 + : inversePatches.length - cachedPosition - 1; + for (let i = stateIndex; i > -1; i--) { + currentState = apply( + currentState as object, + allPatches.inversePatches[i] + ) as S; + history.unshift(currentState); + } + return history; + }, + patches: allPatches, + back: (amount = 1) => { + go(cachedPosition - amount); + }, + forward: (amount = 1) => { + go(cachedPosition + amount); + }, + reset: () => { + setPosition(-1); + setAllPatches( + () => initialPatches ?? { patches: [], inversePatches: [] } + ); + setState(() => initialState); + }, + go, + canBack: () => cachedPosition > 0, + canForward: () => cachedPosition < allPatches.patches.length, + }; + }, [ + cachedPosition, + allPatches, + setPosition, + setAllPatches, + setState, + initialState, + state, + ]); + const cachedSetState = useCallback( + (value: StateValue) => { + setPosition(-1); + if (position !== -1) { + setAllPatches((_allPatches) => { + _allPatches.patches = _allPatches.patches.slice(0, position); + _allPatches.inversePatches = _allPatches.inversePatches.slice( + 0, + position + ); + }); + } + setState(value); + }, + [setState, setPosition, position, setAllPatches] + ); + return [state, cachedSetState, cachedTravels] as Result; }; diff --git a/test/index.test.ts b/test/index.test.ts index 247b422..f514567 100644 --- a/test/index.test.ts +++ b/test/index.test.ts @@ -1,3 +1,350 @@ -test('base', () => { - // test code +import { act, renderHook } from '@testing-library/react'; +import { useTravel } from '../src/index'; + +describe('useTravel', () => { + it('[useTravel] with normal init state', () => { + const { result } = renderHook(() => + useTravel({ todos: [] } as { todos: { name: string }[] }) + ); + let [nextState, setState, controls] = result.current; + expect(nextState).toEqual({ todos: [] }); + act(() => + setState((draft) => { + draft.todos.push({ + name: 'todo 1', + }); + draft.todos.push({ + name: 'todo 2', + }); + }) + ); + [nextState, setState, controls] = result.current; + expect(nextState).toEqual({ + todos: [ + { + name: 'todo 1', + }, + { + name: 'todo 2', + }, + ], + }); + + act(() => + setState((draft) => { + draft.todos.push({ + name: 'todo 3', + }); + }) + ); + [nextState, setState, controls] = result.current; + + act(() => controls.back()); + [nextState, setState, controls] = result.current; + + expect(nextState).toEqual({ + todos: [ + { + name: 'todo 1', + }, + { + name: 'todo 2', + }, + ], + }); + + act(() => controls.forward()); + [nextState, setState, controls] = result.current; + + expect(nextState).toEqual({ + todos: [ + { + name: 'todo 1', + }, + { + name: 'todo 2', + }, + { + name: 'todo 3', + }, + ], + }); + + act(() => controls.go(0)); + [nextState, setState, controls] = result.current; + + expect(nextState).toEqual({ + todos: [], + }); + + act(() => controls.go(1)); + [nextState, setState, controls] = result.current; + + expect(nextState).toEqual({ + todos: [ + { + name: 'todo 1', + }, + { + name: 'todo 2', + }, + ], + }); + + expect(controls.getHistory()).toEqual([ + { + todos: [], + }, + { + todos: [ + { + name: 'todo 1', + }, + { + name: 'todo 2', + }, + ], + }, + { + todos: [ + { + name: 'todo 1', + }, + { + name: 'todo 2', + }, + { + name: 'todo 3', + }, + ], + }, + ]); + + act(() => + setState((draft) => { + draft.todos.push({ + name: 'todo 4', + }); + }) + ); + [nextState, setState, controls] = result.current; + + act(() => + setState((draft) => { + draft.todos.push({ + name: 'todo 5', + }); + }) + ); + [nextState, setState, controls] = result.current; + + expect(nextState).toEqual({ + todos: [ + { + name: 'todo 1', + }, + { + name: 'todo 2', + }, + { + name: 'todo 4', + }, + { + name: 'todo 5', + }, + ], + }); + console.log(controls.patches.patches.length, 'CCCC'); + expect(controls.getHistory()).toEqual([ + { + todos: [], + }, + { + todos: [ + { + name: 'todo 1', + }, + { + name: 'todo 2', + }, + ], + }, + { + todos: [ + { + name: 'todo 1', + }, + { + name: 'todo 2', + }, + { + name: 'todo 4', + }, + ], + }, + { + todos: [ + { + name: 'todo 1', + }, + { + name: 'todo 2', + }, + { + name: 'todo 4', + }, + { + name: 'todo 5', + }, + ], + }, + ]); + + act(() => controls.reset()); + [nextState, setState, controls] = result.current; + expect(nextState).toEqual({ todos: [] }); + }); + + it('[useTravel] with normal init state', () => { + const { result } = renderHook(() => + useTravel({ todos: [] } as { todos: { name: string }[] }) + ); + let [nextState, setState, controls] = result.current; + expect(nextState).toEqual({ todos: [] }); + act(() => + setState((draft) => { + draft.todos.push({ + name: 'todo 1', + }); + draft.todos.push({ + name: 'todo 2', + }); + }) + ); + [nextState, setState, controls] = result.current; + expect(nextState).toEqual({ + todos: [ + { + name: 'todo 1', + }, + { + name: 'todo 2', + }, + ], + }); + + act(() => + setState((draft) => { + draft.todos.push({ + name: 'todo 3', + }); + }) + ); + [nextState, setState, controls] = result.current; + act(() => + setState((draft) => { + draft.todos.push({ + name: 'todo 4', + }); + }) + ); + [nextState, setState, controls] = result.current; + + act(() => controls.go(0)); + [nextState, setState, controls] = result.current; + + expect(controls.position).toBe(0); + expect(controls.patches.patches.length).toBe(3); + expect(controls.canBack()).toBe(false); + expect(controls.canForward()).toBe(true); + + act(() => controls.go(3)); + [nextState, setState, controls] = result.current; + + expect(controls.position).toBe(3); + expect(controls.patches.patches.length).toBe(3); + expect(controls.canBack()).toBe(true); + expect(controls.canForward()).toBe(false); + + act(() => controls.go(1)); + [nextState, setState, controls] = result.current; + + expect(controls.position).toBe(1); + expect(controls.patches.patches.length).toBe(3); + expect(controls.canBack()).toBe(true); + expect(controls.canForward()).toBe(true); + + expect(nextState).toEqual({ + todos: [ + { + name: 'todo 1', + }, + { + name: 'todo 2', + }, + ], + }); + + act(() => + setState((draft) => { + draft.todos.push({ + name: 'todo 5', + }); + }) + ); + [nextState, setState, controls] = result.current; + + act(() => controls.go(2)); + [nextState, setState, controls] = result.current; + + expect(controls.position).toBe(2); + expect(controls.patches.patches.length).toBe(2); + expect(controls.canBack()).toBe(true); + expect(controls.canForward()).toBe(false); + + expect(nextState).toEqual({ + todos: [ + { + name: 'todo 1', + }, + { + name: 'todo 2', + }, + { name: 'todo 5' }, + ], + }); + + const fnWarning = jest.spyOn(console, 'warn'); + act(() => controls.go(3)); + [nextState, setState, controls] = result.current; + expect(fnWarning).toHaveBeenCalledWith(`Can't go forward to position 3`); + expect(nextState).toEqual({ + todos: [ + { + name: 'todo 1', + }, + { + name: 'todo 2', + }, + { name: 'todo 5' }, + ], + }); + + act(() => controls.go(0)); + [nextState, setState, controls] = result.current; + + act(() => controls.back()); + [nextState, setState, controls] = result.current; + + expect(controls.position).toBe(0); + expect(controls.patches.patches.length).toBe(2); + expect(controls.canBack()).toBe(false); + expect(controls.canForward()).toBe(true); + + expect(nextState).toEqual({ + todos: [], + }); + + expect(fnWarning).toHaveBeenCalledWith(`Can't go back to position -1`); + }); }); diff --git a/yarn.lock b/yarn.lock index 56a09db..e9b08fd 100644 --- a/yarn.lock +++ b/yarn.lock @@ -263,6 +263,13 @@ dependencies: "@babel/helper-plugin-utils" "^7.22.5" +"@babel/runtime@^7.12.5": + version "7.24.0" + resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.24.0.tgz#584c450063ffda59697021430cb47101b085951e" + integrity sha512-Chk32uHMg6TnQdvw2e9IlqPpFX/6NLuK0Ys2PqLb7/gL5uFn9mXvK715FGLlOLQrcO4qIkNHkvPGktzzXexsFw== + dependencies: + regenerator-runtime "^0.14.0" + "@babel/template@^7.22.15", "@babel/template@^7.24.0", "@babel/template@^7.3.3": version "7.24.0" resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.24.0.tgz#c6a524aa93a4a05d66aaf31654258fae69d87d50" @@ -746,6 +753,29 @@ dependencies: "@sinonjs/commons" "^3.0.0" +"@testing-library/dom@^9.0.0": + version "9.3.4" + resolved "https://registry.yarnpkg.com/@testing-library/dom/-/dom-9.3.4.tgz#50696ec28376926fec0a1bf87d9dbac5e27f60ce" + integrity sha512-FlS4ZWlp97iiNWig0Muq8p+3rVDjRiYE+YKGbAqXOu9nwJFFOdL00kFpz42M+4huzYi86vAK1sOOfyOG45muIQ== + dependencies: + "@babel/code-frame" "^7.10.4" + "@babel/runtime" "^7.12.5" + "@types/aria-query" "^5.0.1" + aria-query "5.1.3" + chalk "^4.1.0" + dom-accessibility-api "^0.5.9" + lz-string "^1.5.0" + pretty-format "^27.0.2" + +"@testing-library/react@^14.2.1": + version "14.2.1" + resolved "https://registry.yarnpkg.com/@testing-library/react/-/react-14.2.1.tgz#bf69aa3f71c36133349976a4a2da3687561d8310" + integrity sha512-sGdjws32ai5TLerhvzThYFbpnF9XtL65Cjf+gB0Dhr29BGqK+mAeN7SURSdu+eqgET4ANcWoC7FQpkaiGvBr+A== + dependencies: + "@babel/runtime" "^7.12.5" + "@testing-library/dom" "^9.0.0" + "@types/react-dom" "^18.0.0" + "@tootallnate/once@2": version "2.0.0" resolved "https://registry.yarnpkg.com/@tootallnate/once/-/once-2.0.0.tgz#f544a148d3ab35801c1f633a7441fd87c2e484bf" @@ -771,6 +801,11 @@ resolved "https://registry.yarnpkg.com/@tsconfig/node16/-/node16-1.0.4.tgz#0b92dcc0cc1c81f6f306a381f28e31b1a56536e9" integrity sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA== +"@types/aria-query@^5.0.1": + version "5.0.4" + resolved "https://registry.yarnpkg.com/@types/aria-query/-/aria-query-5.0.4.tgz#1a31c3d378850d2778dabb6374d036dcba4ba708" + integrity sha512-rfT93uj5s0PRL7EzccGMs3brplhcrghnDoV26NqKhCAS1hVo+WdNsPvE/yb6ilfr5hi2MEk6d5EWJTKdxg8jVw== + "@types/babel__core@^7.1.14": version "7.20.5" resolved "https://registry.yarnpkg.com/@types/babel__core/-/babel__core-7.20.5.tgz#3df15f27ba85319caa07ba08d0721889bb39c017" @@ -883,11 +918,37 @@ dependencies: undici-types "~5.26.4" +"@types/prop-types@*": + version "15.7.11" + resolved "https://registry.yarnpkg.com/@types/prop-types/-/prop-types-15.7.11.tgz#2596fb352ee96a1379c657734d4b913a613ad563" + integrity sha512-ga8y9v9uyeiLdpKddhxYQkxNDrfvuPrlFb0N1qnZZByvcElJaXthF1UhvCh9TLWJBEHeNtdnbysW7Y6Uq8CVng== + +"@types/react-dom@^18.0.0": + version "18.2.22" + resolved "https://registry.yarnpkg.com/@types/react-dom/-/react-dom-18.2.22.tgz#d332febf0815403de6da8a97e5fe282cbe609bae" + integrity sha512-fHkBXPeNtfvri6gdsMYyW+dW7RXFo6Ad09nLFK0VQWR7yGLai/Cyvyj696gbwYvBnhGtevUG9cET0pmUbMtoPQ== + dependencies: + "@types/react" "*" + +"@types/react@*", "@types/react@^18.2.66": + version "18.2.66" + resolved "https://registry.yarnpkg.com/@types/react/-/react-18.2.66.tgz#d2eafc8c4e70939c5432221adb23d32d76bfe451" + integrity sha512-OYTmMI4UigXeFMF/j4uv0lBBEbongSgptPrHBxqME44h9+yNov+oL6Z3ocJKo0WyXR84sQUNeyIp9MRfckvZpg== + dependencies: + "@types/prop-types" "*" + "@types/scheduler" "*" + csstype "^3.0.2" + "@types/resolve@1.20.2": version "1.20.2" resolved "https://registry.yarnpkg.com/@types/resolve/-/resolve-1.20.2.tgz#97d26e00cd4a0423b4af620abecf3e6f442b7975" integrity sha512-60BCwRFOZCQhDncwQdxxeOEEkbc5dIMccYLwbxsS4TUNeVECQ/pBJ0j09mrHOl/JJvpRPGwO9SvE4nR2Nb/a4Q== +"@types/scheduler@*": + version "0.16.8" + resolved "https://registry.yarnpkg.com/@types/scheduler/-/scheduler-0.16.8.tgz#ce5ace04cfeabe7ef87c0091e50752e36707deff" + integrity sha512-WZLiwShhwLRmeV6zH+GkbOFT6Z6VklCItrDioxUnv+u4Ll+8vKeFySoFyK/0ctcRpOmwAicELfmys1sDc/Rw+A== + "@types/semver@^7.3.12": version "7.5.8" resolved "https://registry.yarnpkg.com/@types/semver/-/semver-7.5.8.tgz#8268a8c57a3e4abd25c165ecd36237db7948a55e" @@ -1120,7 +1181,14 @@ argparse@^2.0.1: resolved "https://registry.yarnpkg.com/argparse/-/argparse-2.0.1.tgz#246f50f3ca78a3240f6c997e8a9bd1eac49e4b38" integrity sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q== -array-buffer-byte-length@^1.0.1: +aria-query@5.1.3: + version "5.1.3" + resolved "https://registry.yarnpkg.com/aria-query/-/aria-query-5.1.3.tgz#19db27cd101152773631396f7a95a3b58c22c35e" + integrity sha512-R5iJ5lkuHybztUfuOAznmboyjWq8O6sqNqtK7CLOqdydi54VNbORp49mb14KbWgG1QD3JFO9hJdZ+y4KutfdOQ== + dependencies: + deep-equal "^2.0.5" + +array-buffer-byte-length@^1.0.0, array-buffer-byte-length@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/array-buffer-byte-length/-/array-buffer-byte-length-1.0.1.tgz#1e5583ec16763540a27ae52eed99ff899223568f" integrity sha512-ahC5W1xgou+KTXix4sAO8Ki12Q+jf4i0+tmk3sC+zgcynshkHxzpXdImBehiUYKKKDwvfFiJl1tZt6ewscS1Mg== @@ -1621,6 +1689,11 @@ cssstyle@^2.3.0: dependencies: cssom "~0.3.6" +csstype@^3.0.2: + version "3.1.3" + resolved "https://registry.yarnpkg.com/csstype/-/csstype-3.1.3.tgz#d80ff294d114fb0e6ac500fbf85b60137d7eff81" + integrity sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw== + cz-conventional-changelog@3.3.0: version "3.3.0" resolved "https://registry.yarnpkg.com/cz-conventional-changelog/-/cz-conventional-changelog-3.3.0.tgz#9246947c90404149b3fe2cf7ee91acad3b7d22d2" @@ -1673,6 +1746,30 @@ dedent@^1.0.0: resolved "https://registry.yarnpkg.com/dedent/-/dedent-1.5.1.tgz#4f3fc94c8b711e9bb2800d185cd6ad20f2a90aff" integrity sha512-+LxW+KLWxu3HW3M2w2ympwtqPrqYRzU8fqi6Fhd18fBALe15blJPI/I4+UHveMVG6lJqB4JNd4UG0S5cnVHwIg== +deep-equal@^2.0.5: + version "2.2.3" + resolved "https://registry.yarnpkg.com/deep-equal/-/deep-equal-2.2.3.tgz#af89dafb23a396c7da3e862abc0be27cf51d56e1" + integrity sha512-ZIwpnevOurS8bpT4192sqAowWM76JDKSHYzMLty3BZGSswgq6pBaH3DhCSW5xVAZICZyKdOBPjwww5wfgT/6PA== + dependencies: + array-buffer-byte-length "^1.0.0" + call-bind "^1.0.5" + es-get-iterator "^1.1.3" + get-intrinsic "^1.2.2" + is-arguments "^1.1.1" + is-array-buffer "^3.0.2" + is-date-object "^1.0.5" + is-regex "^1.1.4" + is-shared-array-buffer "^1.0.2" + isarray "^2.0.5" + object-is "^1.1.5" + object-keys "^1.1.1" + object.assign "^4.1.4" + regexp.prototype.flags "^1.5.1" + side-channel "^1.0.4" + which-boxed-primitive "^1.0.2" + which-collection "^1.0.1" + which-typed-array "^1.1.13" + deep-is@^0.1.3: version "0.1.4" resolved "https://registry.yarnpkg.com/deep-is/-/deep-is-0.1.4.tgz#a6f2dce612fadd2ef1f519b73551f17e85199831" @@ -1759,6 +1856,11 @@ doctrine@^3.0.0: dependencies: esutils "^2.0.2" +dom-accessibility-api@^0.5.9: + version "0.5.16" + resolved "https://registry.yarnpkg.com/dom-accessibility-api/-/dom-accessibility-api-0.5.16.tgz#5a7429e6066eb3664d911e33fb0e45de8eb08453" + integrity sha512-X7BJ2yElsnOJ30pZF4uIIDfBEVgF4XEBxL9Bxhy6dnrm5hkzqmsWHGTiHqRiITNhMyFLyAiWndIJP7Z1NTteDg== + domexception@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/domexception/-/domexception-4.0.0.tgz#4ad1be56ccadc86fc76d033353999a8037d03673" @@ -1857,6 +1959,21 @@ es-errors@^1.0.0, es-errors@^1.2.1, es-errors@^1.3.0: resolved "https://registry.yarnpkg.com/es-errors/-/es-errors-1.3.0.tgz#05f75a25dab98e4fb1dcd5e1472c0546d5057c8f" integrity sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw== +es-get-iterator@^1.1.3: + version "1.1.3" + resolved "https://registry.yarnpkg.com/es-get-iterator/-/es-get-iterator-1.1.3.tgz#3ef87523c5d464d41084b2c3c9c214f1199763d6" + integrity sha512-sPZmqHBe6JIiTfN5q2pEi//TwxmAFHwj/XEuYjTuse78i8KxaqMTTzxPoFKuzRpDpTJ+0NAbpfenkmH2rePtuw== + dependencies: + call-bind "^1.0.2" + get-intrinsic "^1.1.3" + has-symbols "^1.0.3" + is-arguments "^1.1.1" + is-map "^2.0.2" + is-set "^2.0.2" + is-string "^1.0.7" + isarray "^2.0.5" + stop-iteration-iterator "^1.0.0" + es-set-tostringtag@^2.0.3: version "2.0.3" resolved "https://registry.yarnpkg.com/es-set-tostringtag/-/es-set-tostringtag-2.0.3.tgz#8bb60f0a440c2e4281962428438d58545af39777" @@ -2673,7 +2790,7 @@ inquirer@8.2.5: through "^2.3.6" wrap-ansi "^7.0.0" -internal-slot@^1.0.7: +internal-slot@^1.0.4, internal-slot@^1.0.7: version "1.0.7" resolved "https://registry.yarnpkg.com/internal-slot/-/internal-slot-1.0.7.tgz#c06dcca3ed874249881007b0a5523b172a190802" integrity sha512-NGnrKwXzSms2qUUih/ILZ5JBqNTSa1+ZmP6flaIp6KmSElgE9qdndzS3cqjrDovwFdmwsGsLdeFgB6suw+1e9g== @@ -2682,7 +2799,15 @@ internal-slot@^1.0.7: hasown "^2.0.0" side-channel "^1.0.4" -is-array-buffer@^3.0.4: +is-arguments@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/is-arguments/-/is-arguments-1.1.1.tgz#15b3f88fda01f2a97fec84ca761a560f123efa9b" + integrity sha512-8Q7EARjzEnKpt/PCD7e1cgUS0a6X8u5tdSiMqXhojOdoV9TsMsiO+9VLC5vAmO8N7/GmXn7yjR8qnA6bVAEzfA== + dependencies: + call-bind "^1.0.2" + has-tostringtag "^1.0.0" + +is-array-buffer@^3.0.2, is-array-buffer@^3.0.4: version "3.0.4" resolved "https://registry.yarnpkg.com/is-array-buffer/-/is-array-buffer-3.0.4.tgz#7a1f92b3d61edd2bc65d24f130530ea93d7fae98" integrity sha512-wcjaerHw0ydZwfhiKbXJWLDY8A7yV7KhjQOpb83hGgGfId/aQa4TOvwyzn2PuswW2gPCYEL/nEAiSVpdOj1lXw== @@ -2729,7 +2854,7 @@ is-core-module@^2.13.0, is-core-module@^2.13.1: dependencies: hasown "^2.0.0" -is-date-object@^1.0.1: +is-date-object@^1.0.1, is-date-object@^1.0.5: version "1.0.5" resolved "https://registry.yarnpkg.com/is-date-object/-/is-date-object-1.0.5.tgz#0841d5536e724c25597bf6ea62e1bd38298df31f" integrity sha512-9YQaSxsAiSwcvS33MBk3wTCVnWK+HhF8VZR2jRxehM16QcVOdHqPn4VPHmRK4lSr38n9JriurInLcP90xsYNfQ== @@ -2763,6 +2888,11 @@ is-interactive@^1.0.0: resolved "https://registry.yarnpkg.com/is-interactive/-/is-interactive-1.0.0.tgz#cea6e6ae5c870a7b0a0004070b7b587e0252912e" integrity sha512-2HvIEKRoqS62guEC+qBjpvRubdX910WCMuJTZ+I9yvqKU2/12eSL549HMwtabb4oupdj2sMP50k+XJfB/8JE6w== +is-map@^2.0.2, is-map@^2.0.3: + version "2.0.3" + resolved "https://registry.yarnpkg.com/is-map/-/is-map-2.0.3.tgz#ede96b7fe1e270b3c4465e3a465658764926d62e" + integrity sha512-1Qed0/Hr2m+YqxnM09CjA2d/i6YZNfF6R2oRAOj36eUdS6qIV/huPJNSEpKbupewFs+ZsJlxsjjPbc0/afW6Lw== + is-module@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/is-module/-/is-module-1.0.0.tgz#3258fb69f78c14d5b815d664336b4cffb6441591" @@ -2810,6 +2940,11 @@ is-regex@^1.1.4: call-bind "^1.0.2" has-tostringtag "^1.0.0" +is-set@^2.0.2, is-set@^2.0.3: + version "2.0.3" + resolved "https://registry.yarnpkg.com/is-set/-/is-set-2.0.3.tgz#8ab209ea424608141372ded6e0cb200ef1d9d01d" + integrity sha512-iPAjerrse27/ygGLxw+EBR9agv9Y6uLeYVJMu+QNCoouJ1/1ri0mGrcWpfCqFZuzzx3WjtwxG098X+n4OuRkPg== + is-shared-array-buffer@^1.0.2, is-shared-array-buffer@^1.0.3: version "1.0.3" resolved "https://registry.yarnpkg.com/is-shared-array-buffer/-/is-shared-array-buffer-1.0.3.tgz#1237f1cba059cdb62431d378dcc37d9680181688" @@ -2853,6 +2988,11 @@ is-utf8@^0.2.1: resolved "https://registry.yarnpkg.com/is-utf8/-/is-utf8-0.2.1.tgz#4b0da1442104d1b336340e80797e865cf39f7d72" integrity sha512-rMYPYvCzsXywIsldgLaSoPlw5PfoB/ssr7hY4pLfcodrA5M/eArza1a9VmTiNIBNMjOGr1Ow9mTyU2o69U6U9Q== +is-weakmap@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/is-weakmap/-/is-weakmap-2.0.2.tgz#bf72615d649dfe5f699079c54b83e47d1ae19cfd" + integrity sha512-K5pXYOm9wqY1RgjpL3YTkF39tni1XajUIkawTLUo9EZEVUFga5gSQJF8nNS7ZwJQ02y+1YCNYcMh+HIf1ZqE+w== + is-weakref@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/is-weakref/-/is-weakref-1.0.2.tgz#9529f383a9338205e89765e0392efc2f100f06f2" @@ -2860,6 +3000,14 @@ is-weakref@^1.0.2: dependencies: call-bind "^1.0.2" +is-weakset@^2.0.3: + version "2.0.3" + resolved "https://registry.yarnpkg.com/is-weakset/-/is-weakset-2.0.3.tgz#e801519df8c0c43e12ff2834eead84ec9e624007" + integrity sha512-LvIm3/KWzS9oRFHugab7d+M/GcBXuXX5xZkzPmN+NxihdQlZUQ4dWuSV1xR/sq6upL1TJEDrfBgRepHFdBtSNQ== + dependencies: + call-bind "^1.0.7" + get-intrinsic "^1.2.4" + is-windows@^1.0.1: version "1.0.2" resolved "https://registry.yarnpkg.com/is-windows/-/is-windows-1.0.2.tgz#d1850eb9791ecd18e6182ce12a30f396634bb19d" @@ -3314,7 +3462,7 @@ jiti@^1.19.1: resolved "https://registry.yarnpkg.com/jiti/-/jiti-1.21.0.tgz#7c97f8fe045724e136a397f7340475244156105d" integrity sha512-gFqAIbuKyyso/3G2qhiO2OM6shY6EPP/R0+mkDbyspxKazh8BXDC5FiFsUjlczgdNz/vfra0da2y+aHrusLG/Q== -js-tokens@^4.0.0: +"js-tokens@^3.0.0 || ^4.0.0", js-tokens@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499" integrity sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ== @@ -3514,6 +3662,13 @@ longest@^2.0.1: resolved "https://registry.yarnpkg.com/longest/-/longest-2.0.1.tgz#781e183296aa94f6d4d916dc335d0d17aefa23f8" integrity sha512-Ajzxb8CM6WAnFjgiloPsI3bF+WCxcvhdIG3KNA2KN962+tdBsHcuQ4k4qX/EcS/2CRkcc0iAkR956Nib6aXU/Q== +loose-envify@^1.1.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/loose-envify/-/loose-envify-1.4.0.tgz#71ee51fa7be4caec1a63839f7e682d8132d30caf" + integrity sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q== + dependencies: + js-tokens "^3.0.0 || ^4.0.0" + lru-cache@^5.1.1: version "5.1.1" resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-5.1.1.tgz#1da27e6710271947695daf6848e847f01d84b920" @@ -3538,6 +3693,11 @@ lunr@^2.3.9: resolved "https://registry.yarnpkg.com/lunr/-/lunr-2.3.9.tgz#18b123142832337dd6e964df1a5a7707b25d35e1" integrity sha512-zTU3DaZaF3Rt9rhN3uBMGQD3dD2/vFQqnvZCDv4dl5iOzq2IZQqTxu90r4E5J+nP70J3ilqVCrbho2eWaeW8Ow== +lz-string@^1.5.0: + version "1.5.0" + resolved "https://registry.yarnpkg.com/lz-string/-/lz-string-1.5.0.tgz#c1ab50f77887b712621201ba9fd4e3a6ed099941" + integrity sha512-h5bgJWpxJNswbU7qCrV0tIKQCaS3blPDrqKWx+QxzuzL1zGUzij9XCWLrSLsJPu5t+eWA/ycetzYAO5IOMcWAQ== + magic-string@^0.27.0: version "0.27.0" resolved "https://registry.yarnpkg.com/magic-string/-/magic-string-0.27.0.tgz#e4a3413b4bab6d98d2becffd48b4a257effdbbf3" @@ -3731,12 +3891,20 @@ object-inspect@^1.13.1: resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.13.1.tgz#b96c6109324ccfef6b12216a956ca4dc2ff94bc2" integrity sha512-5qoj1RUiKOMsCCNLV1CBiPYE10sziTsnmNxkAI/rZhiD63CF7IqdFGC/XzjWjpSgLf0LxXX3bDFIh0E18f6UhQ== +object-is@^1.1.5: + version "1.1.6" + resolved "https://registry.yarnpkg.com/object-is/-/object-is-1.1.6.tgz#1a6a53aed2dd8f7e6775ff870bea58545956ab07" + integrity sha512-F8cZ+KfGlSGi09lJT7/Nd6KJZ9ygtvYC0/UYYLI9nmQKLMnydpB9yvbv9K1uSkEu7FU9vYPmVwLg328tX+ot3Q== + dependencies: + call-bind "^1.0.7" + define-properties "^1.2.1" + object-keys@^1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/object-keys/-/object-keys-1.1.1.tgz#1c47f272df277f3b1daf061677d9c82e2322c60e" integrity sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA== -object.assign@^4.1.2, object.assign@^4.1.5: +object.assign@^4.1.2, object.assign@^4.1.4, object.assign@^4.1.5: version "4.1.5" resolved "https://registry.yarnpkg.com/object.assign/-/object.assign-4.1.5.tgz#3a833f9ab7fdb80fc9e8d2300c803d216d8fdbb0" integrity sha512-byy+U7gp+FVwmyzKPYhW2h5l3crpmGsxl7X2s8y43IgxvG4g3QZ6CffDtsNQy1WsmZpQbO+ybo0AlW7TY6DcBQ== @@ -3969,6 +4137,15 @@ prettier@^2.8.6: resolved "https://registry.yarnpkg.com/prettier/-/prettier-2.8.8.tgz#e8c5d7e98a4305ffe3de2e1fc4aca1a71c28b1da" integrity sha512-tdN8qQGvNjw4CHbY+XXk0JgCXn9QiF21a55rBe5LJAU+kDyC4WQn4+awm2Xfk2lQMk5fKup9XgzTZtGkjBdP9Q== +pretty-format@^27.0.2: + version "27.5.1" + resolved "https://registry.yarnpkg.com/pretty-format/-/pretty-format-27.5.1.tgz#2181879fdea51a7a5851fb39d920faa63f01d88e" + integrity sha512-Qb1gy5OrP5+zDf2Bvnzdl3jsTf1qXVMazbvCoKhtKqVs4/YK4ozX4gKQJJVyNe+cajNPn0KoC0MC3FUmaHWEmQ== + dependencies: + ansi-regex "^5.0.1" + ansi-styles "^5.0.0" + react-is "^17.0.1" + pretty-format@^29.0.0, pretty-format@^29.7.0: version "29.7.0" resolved "https://registry.yarnpkg.com/pretty-format/-/pretty-format-29.7.0.tgz#ca42c758310f365bfa71a0bda0a807160b776812" @@ -4018,11 +4195,31 @@ randombytes@^2.1.0: dependencies: safe-buffer "^5.1.0" +react-dom@^18.2.0: + version "18.2.0" + resolved "https://registry.yarnpkg.com/react-dom/-/react-dom-18.2.0.tgz#22aaf38708db2674ed9ada224ca4aa708d821e3d" + integrity sha512-6IMTriUmvsjHUjNtEDudZfuDQUoWXVxKHhlEGSk81n4YFS+r/Kl99wXiwlVXtPBtJenozv2P+hxDsw9eA7Xo6g== + dependencies: + loose-envify "^1.1.0" + scheduler "^0.23.0" + +react-is@^17.0.1: + version "17.0.2" + resolved "https://registry.yarnpkg.com/react-is/-/react-is-17.0.2.tgz#e691d4a8e9c789365655539ab372762b0efb54f0" + integrity sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w== + react-is@^18.0.0: version "18.2.0" resolved "https://registry.yarnpkg.com/react-is/-/react-is-18.2.0.tgz#199431eeaaa2e09f86427efbb4f1473edb47609b" integrity sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w== +react@^18.2.0: + version "18.2.0" + resolved "https://registry.yarnpkg.com/react/-/react-18.2.0.tgz#555bd98592883255fa00de14f1151a917b5d77d5" + integrity sha512-/3IjMdb2L9QbBdWiW5e3P2/npwMBaU9mHCSCUzNln0ZCYbcfTsGbTJrU/kGemdH2IWmB2ioZ+zkxtmq6g09fGQ== + dependencies: + loose-envify "^1.1.0" + readable-stream@^3.4.0: version "3.6.2" resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-3.6.2.tgz#56a9b36ea965c00c5a93ef31eb111a0f11056967" @@ -4032,7 +4229,12 @@ readable-stream@^3.4.0: string_decoder "^1.1.1" util-deprecate "^1.0.1" -regexp.prototype.flags@^1.5.2: +regenerator-runtime@^0.14.0: + version "0.14.1" + resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.14.1.tgz#356ade10263f685dda125100cd862c1db895327f" + integrity sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw== + +regexp.prototype.flags@^1.5.1, regexp.prototype.flags@^1.5.2: version "1.5.2" resolved "https://registry.yarnpkg.com/regexp.prototype.flags/-/regexp.prototype.flags-1.5.2.tgz#138f644a3350f981a858c44f6bb1a61ff59be334" integrity sha512-NcDiDkTLuPR+++OCKB0nWafEmhg/Da8aUPLPMQbK+bxKKCm1/S5he+AqYa4PlMCVBalb4/yxIRub6qkEx5yJbw== @@ -4195,6 +4397,13 @@ saxes@^6.0.0: dependencies: xmlchars "^2.2.0" +scheduler@^0.23.0: + version "0.23.0" + resolved "https://registry.yarnpkg.com/scheduler/-/scheduler-0.23.0.tgz#ba8041afc3d30eb206a487b6b384002e4e61fdfe" + integrity sha512-CtuThmgHNg7zIZWAXi3AsyIzA3n4xx7aNyjwC2VJldO2LMVDhFK+63xGqq6CsJH4rTAt6/M+N4GhZiDYPx9eUw== + dependencies: + loose-envify "^1.1.0" + semver@^6.3.0, semver@^6.3.1: version "6.3.1" resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.1.tgz#556d2ef8689146e46dcea4bfdd095f3434dffcb4" @@ -4316,6 +4525,13 @@ stack-utils@^2.0.3: dependencies: escape-string-regexp "^2.0.0" +stop-iteration-iterator@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/stop-iteration-iterator/-/stop-iteration-iterator-1.0.0.tgz#6a60be0b4ee757d1ed5254858ec66b10c49285e4" + integrity sha512-iCGQj+0l0HOdZ2AEeBADlsRC+vsnDsZsbdSiH1yNSjcfKM7fdpCMfqAL/dwF5BLiw/XhRft/Wax6zQbhq2BcjQ== + dependencies: + internal-slot "^1.0.4" + string-length@^4.0.1: version "4.0.2" resolved "https://registry.yarnpkg.com/string-length/-/string-length-4.0.2.tgz#a8a8dc7bd5c1a82b9b3c8b87e125f66871b6e57a" @@ -4696,6 +4912,11 @@ url-parse@^1.5.3: querystringify "^2.1.1" requires-port "^1.0.0" +use-mutative@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/use-mutative/-/use-mutative-1.0.0.tgz#1c99d0a5bb2b8d3563560f398a18c1495a544a94" + integrity sha512-VdeOuHlEs+SJUQpEPv+ADrrFEOb1gGaA5bacSxVZqqfGwq01geoLIRQS6Gxc7cLcOR+o6osChedL1ZeS8pG4KA== + util-deprecate@^1.0.1: version "1.0.2" resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf" @@ -4782,6 +5003,27 @@ which-boxed-primitive@^1.0.2: is-string "^1.0.5" is-symbol "^1.0.3" +which-collection@^1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/which-collection/-/which-collection-1.0.2.tgz#627ef76243920a107e7ce8e96191debe4b16c2a0" + 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.13: + version "1.1.15" + resolved "https://registry.yarnpkg.com/which-typed-array/-/which-typed-array-1.1.15.tgz#264859e9b11a649b388bfaaf4f767df1f779b38d" + integrity sha512-oV0jmFtUky6CXfkqehVvBP/LSWJ2sy4vWMioiENyJLePrBO/yKyV9OyJySfAKosh+RYkIl5zJCNZ8/4JncrpdA== + dependencies: + available-typed-arrays "^1.0.7" + call-bind "^1.0.7" + for-each "^0.3.3" + gopd "^1.0.1" + has-tostringtag "^1.0.2" + which-typed-array@^1.1.14: version "1.1.14" resolved "https://registry.yarnpkg.com/which-typed-array/-/which-typed-array-1.1.14.tgz#1f78a111aee1e131ca66164d8bdc3ab062c95a06"