diff --git a/.gitignore b/.gitignore index c7db0d1e8f..be695425ed 100644 --- a/.gitignore +++ b/.gitignore @@ -57,3 +57,4 @@ src/style - official.css /e2e/specs/.test-results/ /e2e/playwright-report/ /playwright/.cache/ +.DS_Store \ No newline at end of file diff --git a/client/.eslintrc.js b/client/.eslintrc.js index 2f2422bac8..7b8f7bd096 100644 --- a/client/.eslintrc.js +++ b/client/.eslintrc.js @@ -26,5 +26,6 @@ module.exports = { "rules": { 'react/prop-types': ['off'], 'react/display-name': ['off'], + "no-debugger":"off", } } diff --git a/client/package-lock.json b/client/package-lock.json index 8db0845566..b91659ef42 100644 --- a/client/package-lock.json +++ b/client/package-lock.json @@ -18,6 +18,7 @@ "@radix-ui/react-label": "^2.0.0", "@radix-ui/react-slider": "^1.1.1", "@radix-ui/react-tabs": "^1.0.3", + "@tanstack/react-query": "^4.28.0", "@types/jest": "^29.5.0", "@types/node": "^18.15.10", "@types/react": "^18.0.30", @@ -51,12 +52,10 @@ "remark-gfm": "^3.0.1", "remark-math": "^5.1.1", "remark-supersub": "^1.0.0", - "swr": "^2.0.3", "tailwind-merge": "^1.9.1", "tailwindcss-animate": "^1.0.5", "tailwindcss-radix": "^2.8.0", "url": "^0.11.0", - "use-react-screenshot": "github:danny-avila/use-react-screenshot#master", "uuidv4": "^6.2.13" }, "devDependencies": { @@ -66,7 +65,13 @@ "@babel/plugin-transform-runtime": "^7.19.6", "@babel/preset-env": "^7.20.2", "@babel/preset-react": "^7.18.6", + "@babel/preset-typescript": "^7.21.0", "@babel/runtime": "^7.20.13", + "@tanstack/react-query-devtools": "^4.29.0", + "@types/jest": "^29.5.0", + "@types/node": "^18.15.10", + "@types/react": "^18.0.30", + "@types/react-dom": "^18.0.11", "@vitejs/plugin-react": "^3.1.0", "autoprefixer": "^10.4.13", "babel-loader": "^9.1.2", @@ -139,7 +144,9 @@ }, "node_modules/@babel/code-frame": { "version": "7.18.6", - "license": "MIT", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.18.6.tgz", + "integrity": "sha512-TDCmlK5eOvH+eH7cdAFlNXeVJqWIQ7gW9tY1GJIpUtFb6CmjVyq2VM3u71bOyR8CRihcCgMUYoDNyLXao3+70Q==", + "dev": true, "dependencies": { "@babel/highlight": "^7.18.6" }, @@ -498,7 +505,9 @@ }, "node_modules/@babel/helper-validator-identifier": { "version": "7.19.1", - "license": "MIT", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.19.1.tgz", + "integrity": "sha512-awrNfaMtnHUr653GgGEs++LlAvW6w+DcPrOliSMXWCKo597CwL5Acf/wWdNkf/tfEQE3mjkeD1YOVZOUV/od1w==", + "dev": true, "engines": { "node": ">=6.9.0" } @@ -540,7 +549,9 @@ }, "node_modules/@babel/highlight": { "version": "7.18.6", - "license": "MIT", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.18.6.tgz", + "integrity": "sha512-u7stbOuYjaPezCuLj29hNW1v64M2Md2qupEKP1fHc7WdOA3DgLh37suiSrZYY7haUB7iBeQZ9P1uiRF359do3g==", + "dev": true, "dependencies": { "@babel/helper-validator-identifier": "^7.18.6", "chalk": "^2.0.0", @@ -909,11 +920,13 @@ } }, "node_modules/@babel/plugin-syntax-jsx": { - "version": "7.18.6", + "version": "7.21.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.21.4.tgz", + "integrity": "sha512-5hewiLct5OKyh6PLKEYaFclcqtIgCb6bmELouxjF6up5q3Sov7rOayW4RwhbaBL0dit8rA80GNfY+UuDp2mBbQ==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.18.6" + "@babel/helper-plugin-utils": "^7.20.2" }, "engines": { "node": ">=6.9.0" @@ -1016,6 +1029,21 @@ "@babel/core": "^7.0.0-0" } }, + "node_modules/@babel/plugin-syntax-typescript": { + "version": "7.21.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.21.4.tgz", + "integrity": "sha512-xz0D39NvhQn4t4RNsHmDnnsaQizIlUkdtYvLs8La1BlfjQ6JEwxkJGeqJMW2tAXx+q6H+WFuUTXNdYVpEya0YA==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.20.2" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, "node_modules/@babel/plugin-transform-arrow-functions": { "version": "7.20.7", "dev": true, @@ -1570,6 +1598,24 @@ "@babel/core": "^7.0.0-0" } }, + "node_modules/@babel/plugin-transform-typescript": { + "version": "7.21.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typescript/-/plugin-transform-typescript-7.21.3.tgz", + "integrity": "sha512-RQxPz6Iqt8T0uw/WsJNReuBpWpBqs/n7mNo18sKLoTbMp+UrEekhH+pKSVC7gWz+DNjo9gryfV8YzCiT45RgMw==", + "dev": true, + "dependencies": { + "@babel/helper-annotate-as-pure": "^7.18.6", + "@babel/helper-create-class-features-plugin": "^7.21.0", + "@babel/helper-plugin-utils": "^7.20.2", + "@babel/plugin-syntax-typescript": "^7.20.0" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, "node_modules/@babel/plugin-transform-unicode-escapes": { "version": "7.18.10", "dev": true, @@ -1721,6 +1767,25 @@ "@babel/core": "^7.0.0-0" } }, + "node_modules/@babel/preset-typescript": { + "version": "7.21.4", + "resolved": "https://registry.npmjs.org/@babel/preset-typescript/-/preset-typescript-7.21.4.tgz", + "integrity": "sha512-sMLNWY37TCdRH/bJ6ZeeOH1nPuanED7Ai9Y/vH31IPqalioJ6ZNFUWONsakhv4r4n+I6gm5lmoE0olkgib/j/A==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.20.2", + "@babel/helper-validator-option": "^7.21.0", + "@babel/plugin-syntax-jsx": "^7.21.4", + "@babel/plugin-transform-modules-commonjs": "^7.21.2", + "@babel/plugin-transform-typescript": "^7.21.3" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, "node_modules/@babel/regjsgen": { "version": "0.8.0", "dev": true, @@ -2480,7 +2545,9 @@ }, "node_modules/@jest/expect-utils": { "version": "29.5.0", - "license": "MIT", + "resolved": "https://registry.npmjs.org/@jest/expect-utils/-/expect-utils-29.5.0.tgz", + "integrity": "sha512-fmKzsidoXQT2KwnrwE0SQq3uj8Z763vzR8LnLBwC2qYWEFpjX8daRsk6rHUM1QvNlEW/UJXNXm59ztmJJWs2Mg==", + "dev": true, "dependencies": { "jest-get-type": "^29.4.3" }, @@ -2490,7 +2557,9 @@ }, "node_modules/@jest/schemas": { "version": "29.4.3", - "license": "MIT", + "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-29.4.3.tgz", + "integrity": "sha512-VLYKXQmtmuEz6IxJsrZwzG9NvtkQsWNnWMsKxqWNu3+CnfzJQhp0WDDKWLVV9hLKr0l3SLLFRqcYHjhtyuDVxg==", + "dev": true, "dependencies": { "@sinclair/typebox": "^0.25.16" }, @@ -2500,7 +2569,9 @@ }, "node_modules/@jest/types": { "version": "29.5.0", - "license": "MIT", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-29.5.0.tgz", + "integrity": "sha512-qbu7kN6czmVRc3xWFQcAN03RAUamgppVUdXrvl1Wr3jlNF93o9mJbGcDWrwGB6ht44u7efB1qCFgVQmca24Uog==", + "dev": true, "dependencies": { "@jest/schemas": "^29.4.3", "@types/istanbul-lib-coverage": "^2.0.0", @@ -2515,7 +2586,9 @@ }, "node_modules/@jest/types/node_modules/ansi-styles": { "version": "4.3.0", - "license": "MIT", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, "dependencies": { "color-convert": "^2.0.1" }, @@ -2528,7 +2601,9 @@ }, "node_modules/@jest/types/node_modules/chalk": { "version": "4.1.2", - "license": "MIT", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, "dependencies": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" @@ -2542,7 +2617,9 @@ }, "node_modules/@jest/types/node_modules/color-convert": { "version": "2.0.1", - "license": "MIT", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, "dependencies": { "color-name": "~1.1.4" }, @@ -2552,18 +2629,24 @@ }, "node_modules/@jest/types/node_modules/color-name": { "version": "1.1.4", - "license": "MIT" + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true }, "node_modules/@jest/types/node_modules/has-flag": { "version": "4.0.0", - "license": "MIT", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, "engines": { "node": ">=8" } }, "node_modules/@jest/types/node_modules/supports-color": { "version": "7.2.0", - "license": "MIT", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, "dependencies": { "has-flag": "^4.0.0" }, @@ -3187,7 +3270,80 @@ }, "node_modules/@sinclair/typebox": { "version": "0.25.24", - "license": "MIT" + "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.25.24.tgz", + "integrity": "sha512-XJfwUVUKDHF5ugKwIcxEgc9k8b7HbznCp6eUfWgu710hMPNIO4aw4/zB5RogDQz8nd6gyCDpU9O/m6qYEWY6yQ==", + "dev": true + }, + "node_modules/@tanstack/match-sorter-utils": { + "version": "8.8.4", + "resolved": "https://registry.npmjs.org/@tanstack/match-sorter-utils/-/match-sorter-utils-8.8.4.tgz", + "integrity": "sha512-rKH8LjZiszWEvmi01NR72QWZ8m4xmXre0OOwlRGnjU01Eqz/QnN+cqpty2PJ0efHblq09+KilvyR7lsbzmXVEw==", + "dev": true, + "dependencies": { + "remove-accents": "0.4.2" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/kentcdodds" + } + }, + "node_modules/@tanstack/query-core": { + "version": "4.27.0", + "resolved": "https://registry.npmjs.org/@tanstack/query-core/-/query-core-4.27.0.tgz", + "integrity": "sha512-sm+QncWaPmM73IPwFlmWSKPqjdTXZeFf/7aEmWh00z7yl2FjqophPt0dE1EHW9P1giMC5rMviv7OUbSDmWzXXA==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/tannerlinsley" + } + }, + "node_modules/@tanstack/react-query": { + "version": "4.28.0", + "resolved": "https://registry.npmjs.org/@tanstack/react-query/-/react-query-4.28.0.tgz", + "integrity": "sha512-8cGBV5300RHlvYdS4ea+G1JcZIt5CIuprXYFnsWggkmGoC0b5JaqG0fIX3qwDL9PTNkKvG76NGThIWbpXivMrQ==", + "dependencies": { + "@tanstack/query-core": "4.27.0", + "use-sync-external-store": "^1.2.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/tannerlinsley" + }, + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0 || ^18.0.0", + "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0", + "react-native": "*" + }, + "peerDependenciesMeta": { + "react-dom": { + "optional": true + }, + "react-native": { + "optional": true + } + } + }, + "node_modules/@tanstack/react-query-devtools": { + "version": "4.29.0", + "resolved": "https://registry.npmjs.org/@tanstack/react-query-devtools/-/react-query-devtools-4.29.0.tgz", + "integrity": "sha512-bzotqin4Wa/GlPgJ2dI7eggQcbMDLIOwEClHGrkyie76DbT8vEEmEV9Kbh6kriKVSqCLpa9ZrgG/f8/Bx1zIwA==", + "dev": true, + "dependencies": { + "@tanstack/match-sorter-utils": "^8.7.0", + "superjson": "^1.10.0", + "use-sync-external-store": "^1.2.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/tannerlinsley" + }, + "peerDependencies": { + "@tanstack/react-query": "4.28.0", + "react": "^16.8.0 || ^17.0.0 || ^18.0.0", + "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0" + } }, "node_modules/@types/body-parser": { "version": "1.19.2", @@ -3291,25 +3447,33 @@ }, "node_modules/@types/istanbul-lib-coverage": { "version": "2.0.4", - "license": "MIT" + "resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.4.tgz", + "integrity": "sha512-z/QT1XN4K4KYuslS23k62yDIDLwLFkzxOuMplDtObz0+y7VqJCaO2o+SPwHCvLFZh7xazvvoor2tA/hPz9ee7g==", + "dev": true }, "node_modules/@types/istanbul-lib-report": { "version": "3.0.0", - "license": "MIT", + "resolved": "https://registry.npmjs.org/@types/istanbul-lib-report/-/istanbul-lib-report-3.0.0.tgz", + "integrity": "sha512-plGgXAPfVKFoYfa9NpYDAkseG+g6Jr294RqeqcqDixSbU34MZVJRi/P+7Y8GDpzkEwLaGZZOpKIEmeVZNtKsrg==", + "dev": true, "dependencies": { "@types/istanbul-lib-coverage": "*" } }, "node_modules/@types/istanbul-reports": { "version": "3.0.1", - "license": "MIT", + "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.1.tgz", + "integrity": "sha512-c3mAZEuK0lvBp8tmuL74XRKn1+y2dcwOUpH7x4WrF6gk1GIgiluDRgMYQtw2OFcBvAJWlt6ASU3tSqxp0Uu0Aw==", + "dev": true, "dependencies": { "@types/istanbul-lib-report": "*" } }, "node_modules/@types/jest": { "version": "29.5.0", - "license": "MIT", + "resolved": "https://registry.npmjs.org/@types/jest/-/jest-29.5.0.tgz", + "integrity": "sha512-3Emr5VOl/aoBwnWcH/EFQvlSAmjV+XtV9GGu5mwdYew5vhQh0IUZx/60x0TzHDu09Bi7HMx10t/namdJw5QIcg==", + "dev": true, "dependencies": { "expect": "^29.0.0", "pretty-format": "^29.0.0" @@ -3349,7 +3513,9 @@ }, "node_modules/@types/node": { "version": "18.15.11", - "license": "MIT" + "resolved": "https://registry.npmjs.org/@types/node/-/node-18.15.11.tgz", + "integrity": "sha512-E5Kwq2n4SbMzQOn6wnmBjuK9ouqlURrcZDVfbo9ftDDTFt3nk7ZKK4GMOzoYgnpQJKcxwQw+lGaBvvlMo0qN/Q==", + "dev": true }, "node_modules/@types/parse5": { "version": "6.0.3", @@ -3380,7 +3546,9 @@ }, "node_modules/@types/react-dom": { "version": "18.0.11", - "license": "MIT", + "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-18.0.11.tgz", + "integrity": "sha512-O38bPbI2CWtgw/OoQoY+BRelw7uysmXbWvw3nLWO21H1HSh+GOlqPuXshJfjmpNlKiiSDG9cc1JZAaMmVdcTlw==", + "dev": true, "dependencies": { "@types/react": "*" } @@ -3426,7 +3594,9 @@ }, "node_modules/@types/stack-utils": { "version": "2.0.1", - "license": "MIT" + "resolved": "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-2.0.1.tgz", + "integrity": "sha512-Hl219/BT5fLAaz6NDkSuhzasy49dwQS/DSdu4MdggFB8zcXv7vflBI3xp7FEmkmdDkBUI2bPUNeMttp2knYdxw==", + "dev": true }, "node_modules/@types/unist": { "version": "2.0.6", @@ -3446,14 +3616,18 @@ }, "node_modules/@types/yargs": { "version": "17.0.24", - "license": "MIT", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.24.tgz", + "integrity": "sha512-6i0aC7jV6QzQB8ne1joVZ0eSFIstHsCrobmOtghM11yGlH0j43FKL2UhWdELkyps0zuf7qVTUVCCR+tgSlyLLw==", + "dev": true, "dependencies": { "@types/yargs-parser": "*" } }, "node_modules/@types/yargs-parser": { "version": "21.0.0", - "license": "MIT" + "resolved": "https://registry.npmjs.org/@types/yargs-parser/-/yargs-parser-21.0.0.tgz", + "integrity": "sha512-iO9ZQHkZxHn4mSakYV0vFHAVDyEOIJQrV2uZ06HxEPcx+mt8swXoZHIbaaJ2crJYFfErySgktuTZ3BeLz+XmFA==", + "dev": true }, "node_modules/@typescript-eslint/scope-manager": { "version": "5.57.0", @@ -3952,7 +4126,9 @@ }, "node_modules/ansi-styles": { "version": "3.2.1", - "license": "MIT", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, "dependencies": { "color-convert": "^1.9.0" }, @@ -4647,7 +4823,9 @@ }, "node_modules/chalk": { "version": "2.4.2", - "license": "MIT", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dev": true, "dependencies": { "ansi-styles": "^3.2.1", "escape-string-regexp": "^1.0.5", @@ -4700,6 +4878,9 @@ }, "node_modules/ci-info": { "version": "3.8.0", + "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.8.0.tgz", + "integrity": "sha512-eXTggHWSooYhq49F2opQhuHWgzucfF2YgODK4e1566GQs5BIfP30B0oenwBJHfWxAs2fyPB1s7Mg949zLf61Yw==", + "dev": true, "funding": [ { "type": "github", @@ -4774,14 +4955,18 @@ }, "node_modules/color-convert": { "version": "1.9.3", - "license": "MIT", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dev": true, "dependencies": { "color-name": "1.1.3" } }, "node_modules/color-name": { "version": "1.1.3", - "license": "MIT" + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", + "dev": true }, "node_modules/colorette": { "version": "2.0.19", @@ -4923,6 +5108,21 @@ "dev": true, "license": "MIT" }, + "node_modules/copy-anything": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/copy-anything/-/copy-anything-3.0.3.tgz", + "integrity": "sha512-fpW2W/BqEzqPp29QS+MwwfisHCQZtiduTe/m8idFo0xbti9fIZ2WVhAsCv4ggFVH3AgCkVdpoOCtQC6gBrdhjw==", + "dev": true, + "dependencies": { + "is-what": "^4.1.8" + }, + "engines": { + "node": ">=12.13" + }, + "funding": { + "url": "https://github.com/sponsors/mesqueeb" + } + }, "node_modules/copy-to-clipboard": { "version": "3.3.3", "license": "MIT", @@ -5329,7 +5529,9 @@ }, "node_modules/diff-sequences": { "version": "29.4.3", - "license": "MIT", + "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-29.4.3.tgz", + "integrity": "sha512-ofrBgwpPhCD85kMKtE9RYFFq6OC1A89oW2vvgWZNCwxrUpRUILopY7lsYyMDSjc8g6U6aiO0Qubg6r4Wgt5ZnA==", + "dev": true, "engines": { "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } @@ -5713,7 +5915,9 @@ }, "node_modules/escape-string-regexp": { "version": "1.0.5", - "license": "MIT", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", + "dev": true, "engines": { "node": ">=0.8.0" } @@ -6269,7 +6473,9 @@ }, "node_modules/expect": { "version": "29.5.0", - "license": "MIT", + "resolved": "https://registry.npmjs.org/expect/-/expect-29.5.0.tgz", + "integrity": "sha512-yM7xqUrCO2JdpFo4XpM82t+PJBFybdqoQuJLDGeDX2ij8NZzqRHyu3Hp188/JX7SWqud+7t4MUdvcgGBICMHZg==", + "dev": true, "dependencies": { "@jest/expect-utils": "^29.5.0", "jest-get-type": "^29.4.3", @@ -6859,7 +7065,9 @@ }, "node_modules/graceful-fs": { "version": "4.2.11", - "license": "ISC" + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", + "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", + "dev": true }, "node_modules/grapheme-splitter": { "version": "1.0.4", @@ -6895,7 +7103,9 @@ }, "node_modules/has-flag": { "version": "3.0.0", - "license": "MIT", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", + "dev": true, "engines": { "node": ">=4" } @@ -7695,6 +7905,18 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/is-what": { + "version": "4.1.8", + "resolved": "https://registry.npmjs.org/is-what/-/is-what-4.1.8.tgz", + "integrity": "sha512-yq8gMao5upkPoGEU9LsB2P+K3Kt8Q3fQFCGyNCWOAnJAMzEXVV9drYb0TXr42TTliLLhKIBvulgAXgtLLnwzGA==", + "dev": true, + "engines": { + "node": ">=12.13" + }, + "funding": { + "url": "https://github.com/sponsors/mesqueeb" + } + }, "node_modules/is-wsl": { "version": "2.2.0", "dev": true, @@ -7807,7 +8029,9 @@ }, "node_modules/jest-diff": { "version": "29.5.0", - "license": "MIT", + "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-29.5.0.tgz", + "integrity": "sha512-LtxijLLZBduXnHSniy0WMdaHjmQnt3g5sa16W4p0HqukYTTsyTW3GD1q41TyGl5YFXj/5B2U6dlh5FM1LIMgxw==", + "dev": true, "dependencies": { "chalk": "^4.0.0", "diff-sequences": "^29.4.3", @@ -7820,7 +8044,9 @@ }, "node_modules/jest-diff/node_modules/ansi-styles": { "version": "4.3.0", - "license": "MIT", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, "dependencies": { "color-convert": "^2.0.1" }, @@ -7833,7 +8059,9 @@ }, "node_modules/jest-diff/node_modules/chalk": { "version": "4.1.2", - "license": "MIT", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, "dependencies": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" @@ -7847,7 +8075,9 @@ }, "node_modules/jest-diff/node_modules/color-convert": { "version": "2.0.1", - "license": "MIT", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, "dependencies": { "color-name": "~1.1.4" }, @@ -7857,18 +8087,24 @@ }, "node_modules/jest-diff/node_modules/color-name": { "version": "1.1.4", - "license": "MIT" + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true }, "node_modules/jest-diff/node_modules/has-flag": { "version": "4.0.0", - "license": "MIT", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, "engines": { "node": ">=8" } }, "node_modules/jest-diff/node_modules/supports-color": { "version": "7.2.0", - "license": "MIT", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, "dependencies": { "has-flag": "^4.0.0" }, @@ -7878,14 +8114,18 @@ }, "node_modules/jest-get-type": { "version": "29.4.3", - "license": "MIT", + "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-29.4.3.tgz", + "integrity": "sha512-J5Xez4nRRMjk8emnTpWrlkyb9pfRQQanDrvWHhsR1+VUfbwxi30eVcZFlcdGInRibU4G5LwHXpI7IRHU0CY+gg==", + "dev": true, "engines": { "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, "node_modules/jest-matcher-utils": { "version": "29.5.0", - "license": "MIT", + "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-29.5.0.tgz", + "integrity": "sha512-lecRtgm/rjIK0CQ7LPQwzCs2VwW6WAahA55YBuI+xqmhm7LAaxokSB8C97yJeYyT+HvQkH741StzpU41wohhWw==", + "dev": true, "dependencies": { "chalk": "^4.0.0", "jest-diff": "^29.5.0", @@ -7898,7 +8138,9 @@ }, "node_modules/jest-matcher-utils/node_modules/ansi-styles": { "version": "4.3.0", - "license": "MIT", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, "dependencies": { "color-convert": "^2.0.1" }, @@ -7911,7 +8153,9 @@ }, "node_modules/jest-matcher-utils/node_modules/chalk": { "version": "4.1.2", - "license": "MIT", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, "dependencies": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" @@ -7925,7 +8169,9 @@ }, "node_modules/jest-matcher-utils/node_modules/color-convert": { "version": "2.0.1", - "license": "MIT", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, "dependencies": { "color-name": "~1.1.4" }, @@ -7935,18 +8181,24 @@ }, "node_modules/jest-matcher-utils/node_modules/color-name": { "version": "1.1.4", - "license": "MIT" + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true }, "node_modules/jest-matcher-utils/node_modules/has-flag": { "version": "4.0.0", - "license": "MIT", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, "engines": { "node": ">=8" } }, "node_modules/jest-matcher-utils/node_modules/supports-color": { "version": "7.2.0", - "license": "MIT", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, "dependencies": { "has-flag": "^4.0.0" }, @@ -7956,7 +8208,9 @@ }, "node_modules/jest-message-util": { "version": "29.5.0", - "license": "MIT", + "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-29.5.0.tgz", + "integrity": "sha512-Kijeg9Dag6CKtIDA7O21zNTACqD5MD/8HfIV8pdD94vFyFuer52SigdC3IQMhab3vACxXMiFk+yMHNdbqtyTGA==", + "dev": true, "dependencies": { "@babel/code-frame": "^7.12.13", "@jest/types": "^29.5.0", @@ -7974,7 +8228,9 @@ }, "node_modules/jest-message-util/node_modules/ansi-styles": { "version": "4.3.0", - "license": "MIT", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, "dependencies": { "color-convert": "^2.0.1" }, @@ -7987,7 +8243,9 @@ }, "node_modules/jest-message-util/node_modules/chalk": { "version": "4.1.2", - "license": "MIT", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, "dependencies": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" @@ -8001,7 +8259,9 @@ }, "node_modules/jest-message-util/node_modules/color-convert": { "version": "2.0.1", - "license": "MIT", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, "dependencies": { "color-name": "~1.1.4" }, @@ -8011,25 +8271,33 @@ }, "node_modules/jest-message-util/node_modules/color-name": { "version": "1.1.4", - "license": "MIT" + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true }, "node_modules/jest-message-util/node_modules/has-flag": { "version": "4.0.0", - "license": "MIT", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, "engines": { "node": ">=8" } }, "node_modules/jest-message-util/node_modules/slash": { "version": "3.0.0", - "license": "MIT", + "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", + "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", + "dev": true, "engines": { "node": ">=8" } }, "node_modules/jest-message-util/node_modules/supports-color": { "version": "7.2.0", - "license": "MIT", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, "dependencies": { "has-flag": "^4.0.0" }, @@ -8039,7 +8307,9 @@ }, "node_modules/jest-util": { "version": "29.5.0", - "license": "MIT", + "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-29.5.0.tgz", + "integrity": "sha512-RYMgG/MTadOr5t8KdhejfvUU82MxsCu5MF6KuDUHl+NuwzUt+Sm6jJWxTJVrDR1j5M/gJVCPKQEpWXY+yIQ6lQ==", + "dev": true, "dependencies": { "@jest/types": "^29.5.0", "@types/node": "*", @@ -8054,7 +8324,9 @@ }, "node_modules/jest-util/node_modules/ansi-styles": { "version": "4.3.0", - "license": "MIT", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, "dependencies": { "color-convert": "^2.0.1" }, @@ -8067,7 +8339,9 @@ }, "node_modules/jest-util/node_modules/chalk": { "version": "4.1.2", - "license": "MIT", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, "dependencies": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" @@ -8081,7 +8355,9 @@ }, "node_modules/jest-util/node_modules/color-convert": { "version": "2.0.1", - "license": "MIT", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, "dependencies": { "color-name": "~1.1.4" }, @@ -8091,18 +8367,24 @@ }, "node_modules/jest-util/node_modules/color-name": { "version": "1.1.4", - "license": "MIT" + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true }, "node_modules/jest-util/node_modules/has-flag": { "version": "4.0.0", - "license": "MIT", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, "engines": { "node": ">=8" } }, "node_modules/jest-util/node_modules/supports-color": { "version": "7.2.0", - "license": "MIT", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, "dependencies": { "has-flag": "^4.0.0" }, @@ -10782,7 +11064,9 @@ }, "node_modules/pretty-format": { "version": "29.5.0", - "license": "MIT", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.5.0.tgz", + "integrity": "sha512-V2mGkI31qdttvTFX7Mt4efOqHXqJWMu4/r66Xh3Z3BwZaPfPJgp6/gbwoujRpPUtfEF6AUUWx3Jim3GCw5g/Qw==", + "dev": true, "dependencies": { "@jest/schemas": "^29.4.3", "ansi-styles": "^5.0.0", @@ -10794,7 +11078,9 @@ }, "node_modules/pretty-format/node_modules/ansi-styles": { "version": "5.2.0", - "license": "MIT", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", + "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", + "dev": true, "engines": { "node": ">=10" }, @@ -11489,6 +11775,12 @@ "unist-util-visit": "^4.0.0" } }, + "node_modules/remove-accents": { + "version": "0.4.2", + "resolved": "https://registry.npmjs.org/remove-accents/-/remove-accents-0.4.2.tgz", + "integrity": "sha512-7pXIJqJOq5tFgG1A2Zxti3Ht8jJF337m4sowbuHsW30ZnkQFnDzy9qBNhgzX8ZLW4+UBcXiiR7SwR6pokHsxiA==", + "dev": true + }, "node_modules/require-from-string": { "version": "2.0.2", "dev": true, @@ -12068,7 +12360,9 @@ }, "node_modules/stack-utils": { "version": "2.0.6", - "license": "MIT", + "resolved": "https://registry.npmjs.org/stack-utils/-/stack-utils-2.0.6.tgz", + "integrity": "sha512-XlkWvfIm6RmsWtNJx+uqtKLS8eqFbxUg0ZzLXqY0caEy9l7hruX8IpiDnjsLavoBgqCCR71TqWO8MaXYheJ3RQ==", + "dev": true, "dependencies": { "escape-string-regexp": "^2.0.0" }, @@ -12078,7 +12372,9 @@ }, "node_modules/stack-utils/node_modules/escape-string-regexp": { "version": "2.0.0", - "license": "MIT", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz", + "integrity": "sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==", + "dev": true, "engines": { "node": ">=8" } @@ -12268,9 +12564,23 @@ "url": "https://github.com/sponsors/isaacs" } }, + "node_modules/superjson": { + "version": "1.12.2", + "resolved": "https://registry.npmjs.org/superjson/-/superjson-1.12.2.tgz", + "integrity": "sha512-ugvUo9/WmvWOjstornQhsN/sR9mnGtWGYeTxFuqLb4AiT4QdUavjGFRALCPKWWnAiUJ4HTpytj5e0t5HoMRkXg==", + "dev": true, + "dependencies": { + "copy-anything": "^3.0.2" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/supports-color": { "version": "5.5.0", - "license": "MIT", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, "dependencies": { "has-flag": "^3.0.0" }, @@ -12288,19 +12598,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/swr": { - "version": "2.1.1", - "license": "MIT", - "dependencies": { - "use-sync-external-store": "^1.2.0" - }, - "engines": { - "pnpm": "7" - }, - "peerDependencies": { - "react": "^16.11.0 || ^17.0.0 || ^18.0.0" - } - }, "node_modules/tailwind-merge": { "version": "1.11.0", "license": "MIT", @@ -13052,19 +13349,6 @@ } } }, - "node_modules/use-react-screenshot": { - "version": "3.0.0", - "resolved": "git+ssh://git@github.com/danny-avila/use-react-screenshot.git#59260177849fc8d635170835e2f89ae2a126b7b6", - "license": "MIT", - "engines": { - "node": ">=8", - "npm": ">=5" - }, - "peerDependencies": { - "html2canvas": "^1.4.1", - "react": "^18.2.0" - } - }, "node_modules/use-sidecar": { "version": "1.1.2", "license": "MIT", @@ -13692,6 +13976,9 @@ }, "@babel/code-frame": { "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.18.6.tgz", + "integrity": "sha512-TDCmlK5eOvH+eH7cdAFlNXeVJqWIQ7gW9tY1GJIpUtFb6CmjVyq2VM3u71bOyR8CRihcCgMUYoDNyLXao3+70Q==", + "dev": true, "requires": { "@babel/highlight": "^7.18.6" } @@ -13924,7 +14211,10 @@ "dev": true }, "@babel/helper-validator-identifier": { - "version": "7.19.1" + "version": "7.19.1", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.19.1.tgz", + "integrity": "sha512-awrNfaMtnHUr653GgGEs++LlAvW6w+DcPrOliSMXWCKo597CwL5Acf/wWdNkf/tfEQE3mjkeD1YOVZOUV/od1w==", + "dev": true }, "@babel/helper-validator-option": { "version": "7.21.0", @@ -13951,6 +14241,9 @@ }, "@babel/highlight": { "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.18.6.tgz", + "integrity": "sha512-u7stbOuYjaPezCuLj29hNW1v64M2Md2qupEKP1fHc7WdOA3DgLh37suiSrZYY7haUB7iBeQZ9P1uiRF359do3g==", + "dev": true, "requires": { "@babel/helper-validator-identifier": "^7.18.6", "chalk": "^2.0.0", @@ -14156,10 +14449,12 @@ } }, "@babel/plugin-syntax-jsx": { - "version": "7.18.6", + "version": "7.21.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.21.4.tgz", + "integrity": "sha512-5hewiLct5OKyh6PLKEYaFclcqtIgCb6bmELouxjF6up5q3Sov7rOayW4RwhbaBL0dit8rA80GNfY+UuDp2mBbQ==", "dev": true, "requires": { - "@babel/helper-plugin-utils": "^7.18.6" + "@babel/helper-plugin-utils": "^7.20.2" } }, "@babel/plugin-syntax-logical-assignment-operators": { @@ -14218,6 +14513,15 @@ "@babel/helper-plugin-utils": "^7.14.5" } }, + "@babel/plugin-syntax-typescript": { + "version": "7.21.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.21.4.tgz", + "integrity": "sha512-xz0D39NvhQn4t4RNsHmDnnsaQizIlUkdtYvLs8La1BlfjQ6JEwxkJGeqJMW2tAXx+q6H+WFuUTXNdYVpEya0YA==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.20.2" + } + }, "@babel/plugin-transform-arrow-functions": { "version": "7.20.7", "dev": true, @@ -14513,6 +14817,18 @@ "@babel/helper-plugin-utils": "^7.18.9" } }, + "@babel/plugin-transform-typescript": { + "version": "7.21.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typescript/-/plugin-transform-typescript-7.21.3.tgz", + "integrity": "sha512-RQxPz6Iqt8T0uw/WsJNReuBpWpBqs/n7mNo18sKLoTbMp+UrEekhH+pKSVC7gWz+DNjo9gryfV8YzCiT45RgMw==", + "dev": true, + "requires": { + "@babel/helper-annotate-as-pure": "^7.18.6", + "@babel/helper-create-class-features-plugin": "^7.21.0", + "@babel/helper-plugin-utils": "^7.20.2", + "@babel/plugin-syntax-typescript": "^7.20.0" + } + }, "@babel/plugin-transform-unicode-escapes": { "version": "7.18.10", "dev": true, @@ -14632,6 +14948,19 @@ "@babel/plugin-transform-react-pure-annotations": "^7.18.6" } }, + "@babel/preset-typescript": { + "version": "7.21.4", + "resolved": "https://registry.npmjs.org/@babel/preset-typescript/-/preset-typescript-7.21.4.tgz", + "integrity": "sha512-sMLNWY37TCdRH/bJ6ZeeOH1nPuanED7Ai9Y/vH31IPqalioJ6ZNFUWONsakhv4r4n+I6gm5lmoE0olkgib/j/A==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.20.2", + "@babel/helper-validator-option": "^7.21.0", + "@babel/plugin-syntax-jsx": "^7.21.4", + "@babel/plugin-transform-modules-commonjs": "^7.21.2", + "@babel/plugin-transform-typescript": "^7.21.3" + } + }, "@babel/regjsgen": { "version": "0.8.0", "dev": true @@ -14984,18 +15313,27 @@ }, "@jest/expect-utils": { "version": "29.5.0", + "resolved": "https://registry.npmjs.org/@jest/expect-utils/-/expect-utils-29.5.0.tgz", + "integrity": "sha512-fmKzsidoXQT2KwnrwE0SQq3uj8Z763vzR8LnLBwC2qYWEFpjX8daRsk6rHUM1QvNlEW/UJXNXm59ztmJJWs2Mg==", + "dev": true, "requires": { "jest-get-type": "^29.4.3" } }, "@jest/schemas": { "version": "29.4.3", + "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-29.4.3.tgz", + "integrity": "sha512-VLYKXQmtmuEz6IxJsrZwzG9NvtkQsWNnWMsKxqWNu3+CnfzJQhp0WDDKWLVV9hLKr0l3SLLFRqcYHjhtyuDVxg==", + "dev": true, "requires": { "@sinclair/typebox": "^0.25.16" } }, "@jest/types": { "version": "29.5.0", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-29.5.0.tgz", + "integrity": "sha512-qbu7kN6czmVRc3xWFQcAN03RAUamgppVUdXrvl1Wr3jlNF93o9mJbGcDWrwGB6ht44u7efB1qCFgVQmca24Uog==", + "dev": true, "requires": { "@jest/schemas": "^29.4.3", "@types/istanbul-lib-coverage": "^2.0.0", @@ -15007,12 +15345,18 @@ "dependencies": { "ansi-styles": { "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, "requires": { "color-convert": "^2.0.1" } }, "chalk": { "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, "requires": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" @@ -15020,18 +15364,30 @@ }, "color-convert": { "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, "requires": { "color-name": "~1.1.4" } }, "color-name": { - "version": "1.1.4" + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true }, "has-flag": { - "version": "4.0.0" + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true }, "supports-color": { "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, "requires": { "has-flag": "^4.0.0" } @@ -15464,7 +15820,44 @@ } }, "@sinclair/typebox": { - "version": "0.25.24" + "version": "0.25.24", + "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.25.24.tgz", + "integrity": "sha512-XJfwUVUKDHF5ugKwIcxEgc9k8b7HbznCp6eUfWgu710hMPNIO4aw4/zB5RogDQz8nd6gyCDpU9O/m6qYEWY6yQ==", + "dev": true + }, + "@tanstack/match-sorter-utils": { + "version": "8.8.4", + "resolved": "https://registry.npmjs.org/@tanstack/match-sorter-utils/-/match-sorter-utils-8.8.4.tgz", + "integrity": "sha512-rKH8LjZiszWEvmi01NR72QWZ8m4xmXre0OOwlRGnjU01Eqz/QnN+cqpty2PJ0efHblq09+KilvyR7lsbzmXVEw==", + "dev": true, + "requires": { + "remove-accents": "0.4.2" + } + }, + "@tanstack/query-core": { + "version": "4.27.0", + "resolved": "https://registry.npmjs.org/@tanstack/query-core/-/query-core-4.27.0.tgz", + "integrity": "sha512-sm+QncWaPmM73IPwFlmWSKPqjdTXZeFf/7aEmWh00z7yl2FjqophPt0dE1EHW9P1giMC5rMviv7OUbSDmWzXXA==" + }, + "@tanstack/react-query": { + "version": "4.28.0", + "resolved": "https://registry.npmjs.org/@tanstack/react-query/-/react-query-4.28.0.tgz", + "integrity": "sha512-8cGBV5300RHlvYdS4ea+G1JcZIt5CIuprXYFnsWggkmGoC0b5JaqG0fIX3qwDL9PTNkKvG76NGThIWbpXivMrQ==", + "requires": { + "@tanstack/query-core": "4.27.0", + "use-sync-external-store": "^1.2.0" + } + }, + "@tanstack/react-query-devtools": { + "version": "4.29.0", + "resolved": "https://registry.npmjs.org/@tanstack/react-query-devtools/-/react-query-devtools-4.29.0.tgz", + "integrity": "sha512-bzotqin4Wa/GlPgJ2dI7eggQcbMDLIOwEClHGrkyie76DbT8vEEmEV9Kbh6kriKVSqCLpa9ZrgG/f8/Bx1zIwA==", + "dev": true, + "requires": { + "@tanstack/match-sorter-utils": "^8.7.0", + "superjson": "^1.10.0", + "use-sync-external-store": "^1.2.0" + } }, "@types/body-parser": { "version": "1.19.2", @@ -15555,22 +15948,34 @@ } }, "@types/istanbul-lib-coverage": { - "version": "2.0.4" + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.4.tgz", + "integrity": "sha512-z/QT1XN4K4KYuslS23k62yDIDLwLFkzxOuMplDtObz0+y7VqJCaO2o+SPwHCvLFZh7xazvvoor2tA/hPz9ee7g==", + "dev": true }, "@types/istanbul-lib-report": { "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@types/istanbul-lib-report/-/istanbul-lib-report-3.0.0.tgz", + "integrity": "sha512-plGgXAPfVKFoYfa9NpYDAkseG+g6Jr294RqeqcqDixSbU34MZVJRi/P+7Y8GDpzkEwLaGZZOpKIEmeVZNtKsrg==", + "dev": true, "requires": { "@types/istanbul-lib-coverage": "*" } }, "@types/istanbul-reports": { "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.1.tgz", + "integrity": "sha512-c3mAZEuK0lvBp8tmuL74XRKn1+y2dcwOUpH7x4WrF6gk1GIgiluDRgMYQtw2OFcBvAJWlt6ASU3tSqxp0Uu0Aw==", + "dev": true, "requires": { "@types/istanbul-lib-report": "*" } }, "@types/jest": { "version": "29.5.0", + "resolved": "https://registry.npmjs.org/@types/jest/-/jest-29.5.0.tgz", + "integrity": "sha512-3Emr5VOl/aoBwnWcH/EFQvlSAmjV+XtV9GGu5mwdYew5vhQh0IUZx/60x0TzHDu09Bi7HMx10t/namdJw5QIcg==", + "dev": true, "requires": { "expect": "^29.0.0", "pretty-format": "^29.0.0" @@ -15604,7 +16009,10 @@ "version": "0.7.31" }, "@types/node": { - "version": "18.15.11" + "version": "18.15.11", + "resolved": "https://registry.npmjs.org/@types/node/-/node-18.15.11.tgz", + "integrity": "sha512-E5Kwq2n4SbMzQOn6wnmBjuK9ouqlURrcZDVfbo9ftDDTFt3nk7ZKK4GMOzoYgnpQJKcxwQw+lGaBvvlMo0qN/Q==", + "dev": true }, "@types/parse5": { "version": "6.0.3" @@ -15630,6 +16038,9 @@ }, "@types/react-dom": { "version": "18.0.11", + "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-18.0.11.tgz", + "integrity": "sha512-O38bPbI2CWtgw/OoQoY+BRelw7uysmXbWvw3nLWO21H1HSh+GOlqPuXshJfjmpNlKiiSDG9cc1JZAaMmVdcTlw==", + "dev": true, "requires": { "@types/react": "*" } @@ -15668,7 +16079,10 @@ } }, "@types/stack-utils": { - "version": "2.0.1" + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-2.0.1.tgz", + "integrity": "sha512-Hl219/BT5fLAaz6NDkSuhzasy49dwQS/DSdu4MdggFB8zcXv7vflBI3xp7FEmkmdDkBUI2bPUNeMttp2knYdxw==", + "dev": true }, "@types/unist": { "version": "2.0.6" @@ -15685,12 +16099,18 @@ }, "@types/yargs": { "version": "17.0.24", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.24.tgz", + "integrity": "sha512-6i0aC7jV6QzQB8ne1joVZ0eSFIstHsCrobmOtghM11yGlH0j43FKL2UhWdELkyps0zuf7qVTUVCCR+tgSlyLLw==", + "dev": true, "requires": { "@types/yargs-parser": "*" } }, "@types/yargs-parser": { - "version": "21.0.0" + "version": "21.0.0", + "resolved": "https://registry.npmjs.org/@types/yargs-parser/-/yargs-parser-21.0.0.tgz", + "integrity": "sha512-iO9ZQHkZxHn4mSakYV0vFHAVDyEOIJQrV2uZ06HxEPcx+mt8swXoZHIbaaJ2crJYFfErySgktuTZ3BeLz+XmFA==", + "dev": true }, "@typescript-eslint/scope-manager": { "version": "5.57.0", @@ -16015,6 +16435,9 @@ }, "ansi-styles": { "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, "requires": { "color-convert": "^1.9.0" } @@ -16498,6 +16921,9 @@ }, "chalk": { "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dev": true, "requires": { "ansi-styles": "^3.2.1", "escape-string-regexp": "^1.0.5", @@ -16525,7 +16951,10 @@ "dev": true }, "ci-info": { - "version": "3.8.0" + "version": "3.8.0", + "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.8.0.tgz", + "integrity": "sha512-eXTggHWSooYhq49F2opQhuHWgzucfF2YgODK4e1566GQs5BIfP30B0oenwBJHfWxAs2fyPB1s7Mg949zLf61Yw==", + "dev": true }, "cipher-base": { "version": "1.0.4", @@ -16565,12 +16994,18 @@ }, "color-convert": { "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dev": true, "requires": { "color-name": "1.1.3" } }, "color-name": { - "version": "1.1.3" + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", + "dev": true }, "colorette": { "version": "2.0.19", @@ -16667,6 +17102,15 @@ "version": "1.0.6", "dev": true }, + "copy-anything": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/copy-anything/-/copy-anything-3.0.3.tgz", + "integrity": "sha512-fpW2W/BqEzqPp29QS+MwwfisHCQZtiduTe/m8idFo0xbti9fIZ2WVhAsCv4ggFVH3AgCkVdpoOCtQC6gBrdhjw==", + "dev": true, + "requires": { + "is-what": "^4.1.8" + } + }, "copy-to-clipboard": { "version": "3.3.3", "requires": { @@ -16914,7 +17358,10 @@ "version": "5.1.0" }, "diff-sequences": { - "version": "29.4.3" + "version": "29.4.3", + "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-29.4.3.tgz", + "integrity": "sha512-ofrBgwpPhCD85kMKtE9RYFFq6OC1A89oW2vvgWZNCwxrUpRUILopY7lsYyMDSjc8g6U6aiO0Qubg6r4Wgt5ZnA==", + "dev": true }, "diffie-hellman": { "version": "5.0.3", @@ -17182,7 +17629,10 @@ "dev": true }, "escape-string-regexp": { - "version": "1.0.5" + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", + "dev": true }, "eslint": { "version": "8.37.0", @@ -17552,6 +18002,9 @@ }, "expect": { "version": "29.5.0", + "resolved": "https://registry.npmjs.org/expect/-/expect-29.5.0.tgz", + "integrity": "sha512-yM7xqUrCO2JdpFo4XpM82t+PJBFybdqoQuJLDGeDX2ij8NZzqRHyu3Hp188/JX7SWqud+7t4MUdvcgGBICMHZg==", + "dev": true, "requires": { "@jest/expect-utils": "^29.5.0", "jest-get-type": "^29.4.3", @@ -17937,7 +18390,10 @@ } }, "graceful-fs": { - "version": "4.2.11" + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", + "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", + "dev": true }, "grapheme-splitter": { "version": "1.0.4", @@ -17961,7 +18417,10 @@ "dev": true }, "has-flag": { - "version": "3.0.0" + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", + "dev": true }, "has-property-descriptors": { "version": "1.0.0", @@ -18427,6 +18886,12 @@ "call-bind": "^1.0.2" } }, + "is-what": { + "version": "4.1.8", + "resolved": "https://registry.npmjs.org/is-what/-/is-what-4.1.8.tgz", + "integrity": "sha512-yq8gMao5upkPoGEU9LsB2P+K3Kt8Q3fQFCGyNCWOAnJAMzEXVV9drYb0TXr42TTliLLhKIBvulgAXgtLLnwzGA==", + "dev": true + }, "is-wsl": { "version": "2.2.0", "dev": true, @@ -18497,6 +18962,9 @@ }, "jest-diff": { "version": "29.5.0", + "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-29.5.0.tgz", + "integrity": "sha512-LtxijLLZBduXnHSniy0WMdaHjmQnt3g5sa16W4p0HqukYTTsyTW3GD1q41TyGl5YFXj/5B2U6dlh5FM1LIMgxw==", + "dev": true, "requires": { "chalk": "^4.0.0", "diff-sequences": "^29.4.3", @@ -18506,12 +18974,18 @@ "dependencies": { "ansi-styles": { "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, "requires": { "color-convert": "^2.0.1" } }, "chalk": { "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, "requires": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" @@ -18519,18 +18993,30 @@ }, "color-convert": { "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, "requires": { "color-name": "~1.1.4" } }, "color-name": { - "version": "1.1.4" + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true }, "has-flag": { - "version": "4.0.0" + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true }, "supports-color": { "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, "requires": { "has-flag": "^4.0.0" } @@ -18538,10 +19024,16 @@ } }, "jest-get-type": { - "version": "29.4.3" + "version": "29.4.3", + "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-29.4.3.tgz", + "integrity": "sha512-J5Xez4nRRMjk8emnTpWrlkyb9pfRQQanDrvWHhsR1+VUfbwxi30eVcZFlcdGInRibU4G5LwHXpI7IRHU0CY+gg==", + "dev": true }, "jest-matcher-utils": { "version": "29.5.0", + "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-29.5.0.tgz", + "integrity": "sha512-lecRtgm/rjIK0CQ7LPQwzCs2VwW6WAahA55YBuI+xqmhm7LAaxokSB8C97yJeYyT+HvQkH741StzpU41wohhWw==", + "dev": true, "requires": { "chalk": "^4.0.0", "jest-diff": "^29.5.0", @@ -18551,12 +19043,18 @@ "dependencies": { "ansi-styles": { "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, "requires": { "color-convert": "^2.0.1" } }, "chalk": { "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, "requires": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" @@ -18564,18 +19062,30 @@ }, "color-convert": { "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, "requires": { "color-name": "~1.1.4" } }, "color-name": { - "version": "1.1.4" + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true }, "has-flag": { - "version": "4.0.0" + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true }, "supports-color": { "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, "requires": { "has-flag": "^4.0.0" } @@ -18584,6 +19094,9 @@ }, "jest-message-util": { "version": "29.5.0", + "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-29.5.0.tgz", + "integrity": "sha512-Kijeg9Dag6CKtIDA7O21zNTACqD5MD/8HfIV8pdD94vFyFuer52SigdC3IQMhab3vACxXMiFk+yMHNdbqtyTGA==", + "dev": true, "requires": { "@babel/code-frame": "^7.12.13", "@jest/types": "^29.5.0", @@ -18598,12 +19111,18 @@ "dependencies": { "ansi-styles": { "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, "requires": { "color-convert": "^2.0.1" } }, "chalk": { "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, "requires": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" @@ -18611,21 +19130,36 @@ }, "color-convert": { "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, "requires": { "color-name": "~1.1.4" } }, "color-name": { - "version": "1.1.4" + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true }, "has-flag": { - "version": "4.0.0" + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true }, "slash": { - "version": "3.0.0" + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", + "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", + "dev": true }, "supports-color": { "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, "requires": { "has-flag": "^4.0.0" } @@ -18634,6 +19168,9 @@ }, "jest-util": { "version": "29.5.0", + "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-29.5.0.tgz", + "integrity": "sha512-RYMgG/MTadOr5t8KdhejfvUU82MxsCu5MF6KuDUHl+NuwzUt+Sm6jJWxTJVrDR1j5M/gJVCPKQEpWXY+yIQ6lQ==", + "dev": true, "requires": { "@jest/types": "^29.5.0", "@types/node": "*", @@ -18645,12 +19182,18 @@ "dependencies": { "ansi-styles": { "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, "requires": { "color-convert": "^2.0.1" } }, "chalk": { "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, "requires": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" @@ -18658,18 +19201,30 @@ }, "color-convert": { "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, "requires": { "color-name": "~1.1.4" } }, "color-name": { - "version": "1.1.4" + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true }, "has-flag": { - "version": "4.0.0" + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true }, "supports-color": { "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, "requires": { "has-flag": "^4.0.0" } @@ -20106,6 +20661,9 @@ }, "pretty-format": { "version": "29.5.0", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.5.0.tgz", + "integrity": "sha512-V2mGkI31qdttvTFX7Mt4efOqHXqJWMu4/r66Xh3Z3BwZaPfPJgp6/gbwoujRpPUtfEF6AUUWx3Jim3GCw5g/Qw==", + "dev": true, "requires": { "@jest/schemas": "^29.4.3", "ansi-styles": "^5.0.0", @@ -20113,7 +20671,10 @@ }, "dependencies": { "ansi-styles": { - "version": "5.2.0" + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", + "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", + "dev": true } } }, @@ -20535,6 +21096,12 @@ "unist-util-visit": "^4.0.0" } }, + "remove-accents": { + "version": "0.4.2", + "resolved": "https://registry.npmjs.org/remove-accents/-/remove-accents-0.4.2.tgz", + "integrity": "sha512-7pXIJqJOq5tFgG1A2Zxti3Ht8jJF337m4sowbuHsW30ZnkQFnDzy9qBNhgzX8ZLW4+UBcXiiR7SwR6pokHsxiA==", + "dev": true + }, "require-from-string": { "version": "2.0.2", "dev": true @@ -20909,12 +21476,18 @@ }, "stack-utils": { "version": "2.0.6", + "resolved": "https://registry.npmjs.org/stack-utils/-/stack-utils-2.0.6.tgz", + "integrity": "sha512-XlkWvfIm6RmsWtNJx+uqtKLS8eqFbxUg0ZzLXqY0caEy9l7hruX8IpiDnjsLavoBgqCCR71TqWO8MaXYheJ3RQ==", + "dev": true, "requires": { "escape-string-regexp": "^2.0.0" }, "dependencies": { "escape-string-regexp": { - "version": "2.0.0" + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz", + "integrity": "sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==", + "dev": true } } }, @@ -21031,8 +21604,20 @@ } } }, + "superjson": { + "version": "1.12.2", + "resolved": "https://registry.npmjs.org/superjson/-/superjson-1.12.2.tgz", + "integrity": "sha512-ugvUo9/WmvWOjstornQhsN/sR9mnGtWGYeTxFuqLb4AiT4QdUavjGFRALCPKWWnAiUJ4HTpytj5e0t5HoMRkXg==", + "dev": true, + "requires": { + "copy-anything": "^3.0.2" + } + }, "supports-color": { "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, "requires": { "has-flag": "^3.0.0" } @@ -21040,12 +21625,6 @@ "supports-preserve-symlinks-flag": { "version": "1.0.0" }, - "swr": { - "version": "2.1.1", - "requires": { - "use-sync-external-store": "^1.2.0" - } - }, "tailwind-merge": { "version": "1.11.0" }, @@ -21494,11 +22073,6 @@ "use-isomorphic-layout-effect": "^1.1.1" } }, - "use-react-screenshot": { - "version": "git+ssh://git@github.com/danny-avila/use-react-screenshot.git#59260177849fc8d635170835e2f89ae2a126b7b6", - "from": "use-react-screenshot@github:danny-avila/use-react-screenshot#master", - "requires": {} - }, "use-sidecar": { "version": "1.1.2", "requires": { diff --git a/client/package.json b/client/package.json index 9323b1a341..f0c82bc165 100644 --- a/client/package.json +++ b/client/package.json @@ -30,6 +30,7 @@ "@radix-ui/react-label": "^2.0.0", "@radix-ui/react-slider": "^1.1.1", "@radix-ui/react-tabs": "^1.0.3", + "@tanstack/react-query": "^4.28.0", "@types/jest": "^29.5.0", "@types/node": "^18.15.10", "@types/react": "^18.0.30", @@ -63,7 +64,6 @@ "remark-gfm": "^3.0.1", "remark-math": "^5.1.1", "remark-supersub": "^1.0.0", - "swr": "^2.0.3", "tailwind-merge": "^1.9.1", "tailwindcss-animate": "^1.0.5", "tailwindcss-radix": "^2.8.0", @@ -77,7 +77,13 @@ "@babel/plugin-transform-runtime": "^7.19.6", "@babel/preset-env": "^7.20.2", "@babel/preset-react": "^7.18.6", + "@babel/preset-typescript": "^7.21.0", "@babel/runtime": "^7.20.13", + "@tanstack/react-query-devtools": "^4.29.0", + "@types/jest": "^29.5.0", + "@types/node": "^18.15.10", + "@types/react": "^18.0.30", + "@types/react-dom": "^18.0.11", "@vitejs/plugin-react": "^3.1.0", "autoprefixer": "^10.4.13", "babel-loader": "^9.1.2", diff --git a/client/src/App.jsx b/client/src/App.jsx index cca1de99a8..9fe0fe22cb 100644 --- a/client/src/App.jsx +++ b/client/src/App.jsx @@ -1,15 +1,13 @@ -import React, { useEffect, useState } from 'react'; +import { useEffect } from 'react'; import { createBrowserRouter, RouterProvider, Navigate } from 'react-router-dom'; import Root from './routes/Root'; import Chat from './routes/Chat'; import Search from './routes/Search'; import store from './store'; -import userAuth from './utils/userAuth'; import { useRecoilState, useSetRecoilState } from 'recoil'; - import { ScreenshotProvider } from './utils/screenshotContext.jsx'; - -import axios from 'axios'; +import { useGetSearchEnabledQuery, useGetUserQuery, useGetEndpointsQuery, useGetPresetsQuery} from '~/data-provider'; +import {ReactQueryDevtools} from '@tanstack/react-query-devtools'; const router = createBrowserRouter([ { @@ -43,58 +41,52 @@ const App = () => { const setEndpointsConfig = useSetRecoilState(store.endpointsConfig); const setPresets = useSetRecoilState(store.presets); + const searchEnabledQuery = useGetSearchEnabledQuery(); + const userQuery = useGetUserQuery(); + const endpointsQuery = useGetEndpointsQuery(); + const presetsQuery = useGetPresetsQuery(); + useEffect(() => { - // fetch if seatch enabled - axios - .get('/api/search/enable', { - timeout: 1000, - withCredentials: true - }) - .then(res => { - setIsSearchEnabled(res.data); - }); + if(endpointsQuery.data) { + setEndpointsConfig(endpointsQuery.data); + } else if(endpointsQuery.isError) { + console.error("Failed to get endpoints", endpointsQuery.error); + window.location.href = '/auth/login'; + } + }, [endpointsQuery.data, endpointsQuery.isError]); - // fetch user - userAuth() - .then(user => setUser(user)) - .catch(err => console.log(err)); + useEffect(() => { + if(presetsQuery.data) { + setPresets(presetsQuery.data); + } else if(presetsQuery.isError) { + console.error("Failed to get presets", presetsQuery.error); + window.location.href = '/auth/login'; + } + }, [presetsQuery.data, presetsQuery.isError]); - // fetch models - axios - .get('/api/endpoints', { - timeout: 1000, - withCredentials: true - }) - .then(({ data }) => { - setEndpointsConfig(data); - }) - .catch(error => { - console.error(error); - console.log('Not login!'); - window.location.href = '/auth/login'; - }); + useEffect(() => { + if (searchEnabledQuery.data) { + setIsSearchEnabled(searchEnabledQuery.data); + } else if(searchEnabledQuery.isError) { + console.error("Failed to get search enabled", searchEnabledQuery.error); + } + }, [searchEnabledQuery.data, searchEnabledQuery.isError]); - // fetch presets - axios - .get('/api/presets', { - timeout: 1000, - withCredentials: true - }) - .then(({ data }) => { - setPresets(data); - }) - .catch(error => { - console.error(error); - console.log('Not login!'); - window.location.href = '/auth/login'; - }); - }, []); + useEffect(() => { + if (userQuery.data) { + setUser(userQuery.data); + } else if(userQuery.isError) { + console.error("Failed to get user", userQuery.error); + window.location.href = '/auth/login'; + } + }, [userQuery.data, userQuery.isError]); if (user) return ( -
+ <> -
+ + ); else return
; }; diff --git a/client/src/components/Conversations/Conversation.jsx b/client/src/components/Conversations/Conversation.jsx index c95c9d1f4b..ed13eea268 100644 --- a/client/src/components/Conversations/Conversation.jsx +++ b/client/src/components/Conversations/Conversation.jsx @@ -1,10 +1,9 @@ -import React, { useState, useRef } from 'react'; +import { useState, useRef, useEffect} from 'react'; import { useRecoilState, useSetRecoilState } from 'recoil'; - +import { useUpdateConversationMutation } from '~/data-provider'; import RenameButton from './RenameButton'; import DeleteButton from './DeleteButton'; import ConvoIcon from '../svg/ConvoIcon'; -import manualSWR from '~/utils/fetchers'; import store from '~/store'; @@ -15,6 +14,8 @@ export default function Conversation({ conversation, retainView }) { const { refreshConversations } = store.useConversations(); const { switchToConversation } = store.useConversation(); + const updateConvoMutation = useUpdateConversationMutation(currentConversation?.conversationId); + const [renaming, setRenaming] = useState(false); const inputRef = useRef(null); @@ -22,8 +23,6 @@ export default function Conversation({ conversation, retainView }) { const [titleInput, setTitleInput] = useState(title); - const rename = manualSWR(`/api/convos/update`, 'post'); - const clickHandler = async () => { if (currentConversation?.conversationId === conversationId) { return; @@ -59,15 +58,20 @@ export default function Conversation({ conversation, retainView }) { if (titleInput === title) { return; } - rename.trigger({ conversationId, title: titleInput }).then(() => { + updateConvoMutation.mutate({ conversationId, title: titleInput }); + }; + + useEffect(() => { + if (updateConvoMutation.isSuccess) { refreshConversations(); - if (conversationId == currentConversation?.conversationId) + if (conversationId == currentConversation?.conversationId) { setCurrentConversation(prevState => ({ ...prevState, title: titleInput })); - }); - }; + } + } + }, [updateConvoMutation.isSuccess]); const handleKeyDown = e => { if (e.key === 'Enter') { diff --git a/client/src/components/Conversations/DeleteButton.jsx b/client/src/components/Conversations/DeleteButton.jsx index 079a1e8296..5789892525 100644 --- a/client/src/components/Conversations/DeleteButton.jsx +++ b/client/src/components/Conversations/DeleteButton.jsx @@ -1,8 +1,8 @@ -import React from 'react'; +import { useEffect } from 'react'; import TrashIcon from '../svg/TrashIcon'; import CrossIcon from '../svg/CrossIcon'; -import manualSWR from '~/utils/fetchers'; import { useRecoilValue } from 'recoil'; +import { useDeleteConversationMutation } from '~/data-provider'; import store from '~/store'; @@ -10,13 +10,23 @@ export default function DeleteButton({ conversationId, renaming, cancelHandler, const currentConversation = useRecoilValue(store.conversation) || {}; const { newConversation } = store.useConversation(); const { refreshConversations } = store.useConversations(); - const { trigger } = manualSWR(`/api/convos/clear`, 'post', () => { - if (currentConversation?.conversationId == conversationId) newConversation(); - refreshConversations(); - retainView(); - }); - const clickHandler = () => trigger({ conversationId, source: 'button' }); + const deleteConvoMutation = useDeleteConversationMutation(conversationId); + + useEffect(() => { + if(deleteConvoMutation.isSuccess) { + if (currentConversation?.conversationId == conversationId) newConversation(); + + refreshConversations(); + retainView(); + } + }, [deleteConvoMutation.isSuccess]); + + + const clickHandler = () => { + deleteConvoMutation.mutate({conversationId, source: 'button' }); + }; + const handler = renaming ? cancelHandler : clickHandler; return ( diff --git a/client/src/components/Endpoints/BingAI/Settings.jsx b/client/src/components/Endpoints/BingAI/Settings.jsx index 09fc00951f..07c28dccd7 100644 --- a/client/src/components/Endpoints/BingAI/Settings.jsx +++ b/client/src/components/Endpoints/BingAI/Settings.jsx @@ -1,22 +1,15 @@ -import React, { useEffect, useState } from 'react'; +import { useEffect, useState } from 'react'; import TextareaAutosize from 'react-textarea-autosize'; -import { Input } from '~/components/ui/Input.tsx'; import { Label } from '~/components/ui/Label.tsx'; import { Checkbox } from '~/components/ui/Checkbox.tsx'; -import SelectDropdown from '../../ui/SelectDropDown'; -import { axiosPost } from '~/utils/fetchers.js'; +import SelectDropDown from '../../ui/SelectDropDown'; import { cn } from '~/utils/'; -import debounce from 'lodash/debounce'; -// import ModelDropDown from '../../ui/ModelDropDown'; -// import { Slider } from '~/components/ui/Slider.tsx'; -// import OptionHover from './OptionHover'; -// import { HoverCard, HoverCardTrigger } from '~/components/ui/HoverCard.tsx'; +import useDebounce from '~/hooks/useDebounce'; +import { useUpdateTokenCountMutation } from '~/data-provider'; + const defaultTextProps = 'rounded-md border border-gray-200 focus:border-slate-400 focus:bg-gray-50 bg-transparent text-sm shadow-[0_0_10px_rgba(0,0,0,0.05)] outline-none placeholder:text-gray-400 focus:outline-none focus:ring-gray-400 focus:ring-opacity-20 focus:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50 dark:border-gray-500 dark:bg-gray-700 focus:dark:bg-gray-600 dark:text-gray-50 dark:shadow-[0_0_15px_rgba(0,0,0,0.10)] dark:focus:border-gray-400 dark:focus:outline-none dark:focus:ring-0 dark:focus:ring-gray-400 dark:focus:ring-offset-0'; -const optionText = - 'p-0 shadow-none text-right pr-1 h-8 border-transparent focus:ring-[#10a37f] focus:ring-offset-0 focus:ring-opacity-100 hover:bg-gray-800/10 dark:hover:bg-white/10 focus:bg-gray-800/10 dark:focus:bg-white/10 transition-colors'; - function Settings(props) { const { readonly, context, systemMessage, jailbreak, toneStyle, setOption } = props; const [tokenCount, setTokenCount] = useState(0); @@ -25,31 +18,26 @@ function Settings(props) { const setSystemMessage = setOption('systemMessage'); const setJailbreak = setOption('jailbreak'); const setToneStyle = value => setOption('toneStyle')(value.toLowerCase()); + const debouncedContext = useDebounce(context, 250); + const updateTokenCountMutation = useUpdateTokenCountMutation(); - // useEffect to update token count - - useEffect(() => { - if (!context || context.trim() === '') { + useEffect(() => { + if (!debouncedContext || debouncedContext.trim() === '') { setTokenCount(0); return; } - const debouncedPost = debounce(axiosPost, 250); const handleTextChange = context => { - debouncedPost({ - url: '/api/tokenizer', - arg: { text: context }, - callback: data => { + updateTokenCountMutation.mutate({ text: context }, { + onSuccess: data => { setTokenCount(data.count); } }); }; - handleTextChange(context); - return () => debouncedPost.cancel(); - }, [context]); + handleTextChange(debouncedContext); + }, [debouncedContext]); - // console.log('data', data); return ( <> @@ -62,7 +50,7 @@ function Settings(props) { > Tone Style (default: fast) - )} - {/* - - - - */} diff --git a/client/src/components/Endpoints/OpenAI/Settings.jsx b/client/src/components/Endpoints/OpenAI/Settings.jsx index d068b76279..2b95ea1c70 100644 --- a/client/src/components/Endpoints/OpenAI/Settings.jsx +++ b/client/src/components/Endpoints/OpenAI/Settings.jsx @@ -1,12 +1,10 @@ -import React from 'react'; import { useRecoilValue } from 'recoil'; import TextareaAutosize from 'react-textarea-autosize'; -import SelectDropdown from '../../ui/SelectDropDown'; +import SelectDropDown from '../../ui/SelectDropDown'; import { Input } from '~/components/ui/Input.tsx'; import { Label } from '~/components/ui/Label.tsx'; import { Slider } from '~/components/ui/Slider.tsx'; import { InputNumber } from '~/components/ui/InputNumber.tsx'; -// import { InputNumber } from '../../ui/InputNumber'; import OptionHover from './OptionHover'; import { HoverCard, HoverCardTrigger } from '~/components/ui/HoverCard.tsx'; import { cn } from '~/utils/'; @@ -38,7 +36,7 @@ function Settings(props) {
- - {/* - setModel(e.target.value)} - placeholder="Set a custom name for ChatGPT" - className={cn( - defaultTextProps, - 'flex h-10 max-h-10 w-full resize-none px-3 py-2 focus:outline-none focus:ring-0 focus:ring-opacity-0 focus:ring-offset-0' - )} - /> */}
@@ -160,43 +131,6 @@ function Settings(props) { side="left" /> - - {/* - -
- - setMaxTokens(e.target.value)} - className={cn( - defaultTextProps, - cn(optionText, 'h-auto w-12 border-0 group-hover/temp:border-gray-200') - )} - /> -
- setMaxTokens(value[0])} - max={2048} // should be dynamic to the currently selected model - min={1} - step={1} - className="flex h-4 w-full" - /> -
- -
*/} -
diff --git a/client/src/components/Endpoints/SaveAsPresetDialog.jsx b/client/src/components/Endpoints/SaveAsPresetDialog.jsx index e06e9ad807..9fd51153e9 100644 --- a/client/src/components/Endpoints/SaveAsPresetDialog.jsx +++ b/client/src/components/Endpoints/SaveAsPresetDialog.jsx @@ -1,19 +1,18 @@ -import React, { useEffect, useState } from 'react'; -import { useSetRecoilState, useRecoilValue } from 'recoil'; -import axios from 'axios'; +import { useEffect, useState } from 'react'; +import { useRecoilValue } from 'recoil'; import DialogTemplate from '../ui/DialogTemplate'; import { Dialog } from '../ui/Dialog.tsx'; import { Input } from '../ui/Input.tsx'; import { Label } from '../ui/Label.tsx'; import { cn } from '~/utils/'; import cleanupPreset from '~/utils/cleanupPreset'; - +import { useCreatePresetMutation } from '~/data-provider'; import store from '~/store'; const SaveAsPresetDialog = ({ open, onOpenChange, preset }) => { const [title, setTitle] = useState(preset?.title || 'My Preset'); - const setPresets = useSetRecoilState(store.presets); const endpointsFilter = useRecoilValue(store.endpointsFilter); + const createPresetMutation = useCreatePresetMutation(); const defaultTextProps = 'rounded-md border border-gray-300 bg-transparent text-sm shadow-[0_0_10px_rgba(0,0,0,0.10)] outline-none placeholder:text-gray-400 focus:outline-none focus:ring-gray-400 focus:ring-opacity-20 focus:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50 dark:border-gray-400 dark:bg-gray-700 dark:text-gray-50 dark:shadow-[0_0_15px_rgba(0,0,0,0.10)] dark:focus:border-gray-400 dark:focus:outline-none dark:focus:ring-0 dark:focus:ring-gray-400 dark:focus:ring-offset-0'; @@ -26,15 +25,7 @@ const SaveAsPresetDialog = ({ open, onOpenChange, preset }) => { }, endpointsFilter }); - - axios({ - method: 'post', - url: '/api/presets', - data: _preset, - withCredentials: true - }).then(res => { - setPresets(res?.data); - }); + createPresetMutation.mutate(_preset); }; useEffect(() => { diff --git a/client/src/components/Input/BingAIOptions/index.jsx b/client/src/components/Input/BingAIOptions/index.jsx index b552977ef2..a5efc1ce02 100644 --- a/client/src/components/Input/BingAIOptions/index.jsx +++ b/client/src/components/Input/BingAIOptions/index.jsx @@ -1,10 +1,10 @@ -import React, { useState, useEffect } from 'react'; -import { useRecoilValue, useRecoilState } from 'recoil'; +import { useState } from 'react'; +import { useRecoilState } from 'recoil'; import { cn } from '~/utils'; import { Button } from '../../ui/Button.tsx'; import { Settings2 } from 'lucide-react'; import { Tabs, TabsList, TabsTrigger } from '../../ui/Tabs.tsx'; -import SelectDropdown from '../../ui/SelectDropDown'; +import SelectDropDown from '../../ui/SelectDropDown'; import Settings from '../../Endpoints/BingAI/Settings.jsx'; import EndpointOptionsPopover from '../../Endpoints/EndpointOptionsPopover'; import SaveAsPresetDialog from '../../Endpoints/SaveAsPresetDialog'; @@ -18,25 +18,12 @@ function BingAIOptions() { const { endpoint, conversationId } = conversation; const { toneStyle, context, systemMessage, jailbreak } = conversation; - // useEffect(() => { - // if (endpoint !== 'bingAI') return; - - // const mustInAdvancedMode = context !== null || systemMessage !== null; - - // if (mustInAdvancedMode && !advancedMode) setAdvancedMode(true); - // }, [conversation, advancedMode]); - if (endpoint !== 'bingAI') return null; if (conversationId !== 'new') return null; const triggerAdvancedMode = () => setAdvancedMode(prev => !prev); const switchToSimpleMode = () => { - // setConversation(prevState => ({ - // ...prevState, - // context: null, - // systemMessage: null - // })); setAdvancedMode(false); }; @@ -68,7 +55,7 @@ function BingAIOptions() { (!advancedMode ? ' show' : '') } > - setOption('jailbreak')(value === 'Sydney')} diff --git a/client/src/components/Input/ChatGPTOptions/index.jsx b/client/src/components/Input/ChatGPTOptions/index.jsx index 23befa36b9..e0f89f15b6 100644 --- a/client/src/components/Input/ChatGPTOptions/index.jsx +++ b/client/src/components/Input/ChatGPTOptions/index.jsx @@ -1,6 +1,6 @@ import React, { useEffect } from 'react'; import { useRecoilState, useRecoilValue } from 'recoil'; -import SelectDropdown from '../../ui/SelectDropDown.jsx'; +import SelectDropDown from '../../ui/SelectDropDown'; import { cn } from '~/utils/'; import store from '~/store'; @@ -12,21 +12,11 @@ function ChatGPTOptions() { const endpointsConfig = useRecoilValue(store.endpointsConfig); - // useEffect(() => { - // if (endpoint !== 'chatGPTBrowser') return; - // }, [conversation]); - if (endpoint !== 'chatGPTBrowser') return null; if (conversationId !== 'new') return null; const models = endpointsConfig?.['chatGPTBrowser']?.['availableModels'] || []; - // const modelMap = new Map([ - // ['Default (GPT-3.5)', 'text-davinci-002-render-sha'], - // ['Legacy (GPT-3.5)', 'text-davinci-002-render-paid'], - // ['GPT-4', 'gpt-4'] - // ]); - const setOption = param => newValue => { let update = {}; update[param] = newValue; @@ -41,7 +31,7 @@ function ChatGPTOptions() { return (
- ({getPresetTitle()}) - - {/* */}
@@ -159,7 +157,7 @@ export default function NewConversationMenu() { title="Clear presets" description="Are you sure you want to clear all presets? This is irreversible." selection={{ - selectHandler: clearPreset, + selectHandler: clearAllPresets, selectClasses: 'bg-red-600 hover:bg-red-700 dark:hover:bg-red-800 text-white', selectText: 'Clear' }} @@ -176,7 +174,7 @@ export default function NewConversationMenu() { presets={presets} onSelect={onSelectPreset} onChangePreset={onChangePreset} - onDeletePreset={clearPresetsTrigger} + onDeletePreset={onDeletePreset} /> ) : ( No preset yet. diff --git a/client/src/components/Input/OpenAIOptions/index.jsx b/client/src/components/Input/OpenAIOptions/index.jsx index ddf3071254..1167abe356 100644 --- a/client/src/components/Input/OpenAIOptions/index.jsx +++ b/client/src/components/Input/OpenAIOptions/index.jsx @@ -1,7 +1,7 @@ -import React, { useEffect, useState } from 'react'; +import { useState } from 'react'; import { Settings2 } from 'lucide-react'; import { useRecoilState, useRecoilValue } from 'recoil'; -import SelectDropdown from '../../ui/SelectDropDown'; +import SelectDropDown from '../../ui/SelectDropDown'; import EndpointOptionsPopover from '../../Endpoints/EndpointOptionsPopover'; import SaveAsPresetDialog from '../../Endpoints/SaveAsPresetDialog'; import { Button } from '../../ui/Button.tsx'; @@ -21,20 +21,6 @@ function OpenAIOptions() { const endpointsConfig = useRecoilValue(store.endpointsConfig); - // useEffect(() => { - // if (endpoint !== 'openAI') return; - - // const mustInAdvancedMode = - // chatGptLabel !== null || - // promptPrefix !== null || - // temperature !== 1 || - // top_p !== 1 || - // presence_penalty !== 0 || - // frequency_penalty !== 0; - - // if (mustInAdvancedMode && !advancedMode) setAdvancedMode(true); - // }, [conversation, advancedMode]); - if (endpoint !== 'openAI') return null; if (conversationId !== 'new') return null; @@ -43,15 +29,6 @@ function OpenAIOptions() { const triggerAdvancedMode = () => setAdvancedMode(prev => !prev); const switchToSimpleMode = () => { - // setConversation(prevState => ({ - // ...prevState, - // chatGptLabel: null, - // promptPrefix: null, - // temperature: 1, - // top_p: 1, - // presence_penalty: 0, - // frequency_penalty: 0 - // })); setAdvancedMode(false); }; @@ -79,17 +56,7 @@ function OpenAIOptions() { (!advancedMode ? ' show' : '') } > - {/* */} - { if (blinker && !abortScroll) { @@ -99,10 +98,9 @@ export default function Message({ const clickSearchResult = async () => { if (!searchResult) return; - const convoResponse = await fetchById('convos', message.conversationId); - const convo = convoResponse.data; - - switchToConversation(convo); + getConversationQuery.refetch(message.conversationId).then((response) => { + switchToConversation(response.data); + }); }; return ( diff --git a/client/src/components/Nav/ClearConvos.jsx b/client/src/components/Nav/ClearConvos.jsx index 3cf6291c48..1236631d9f 100644 --- a/client/src/components/Nav/ClearConvos.jsx +++ b/client/src/components/Nav/ClearConvos.jsx @@ -1,27 +1,27 @@ -import React from 'react'; +import { useEffect } from 'react'; import store from '~/store'; import TrashIcon from '../svg/TrashIcon'; -import { useSWRConfig } from 'swr'; -import manualSWR from '~/utils/fetchers'; import { Dialog, DialogTrigger } from '../ui/Dialog.tsx'; import DialogTemplate from '../ui/DialogTemplate'; +import { useClearConversationsMutation } from '~/data-provider'; export default function ClearConvos() { const { newConversation } = store.useConversation(); const { refreshConversations } = store.useConversations(); - const { mutate } = useSWRConfig(); - - const { trigger } = manualSWR(`/api/convos/clear`, 'post', () => { - newConversation(); - refreshConversations(); - mutate(`/api/convos`); - }); + const clearConvosMutation = useClearConversationsMutation(); const clickHandler = () => { console.log('Clearing conversations...'); - trigger({}); + clearConvosMutation.mutate(); }; + useEffect(() => { + if (clearConvosMutation.isSuccess) { + newConversation(); + refreshConversations(); + } + }, [clearConvosMutation.isSuccess]); + return ( diff --git a/client/src/components/Nav/NavLinks.jsx b/client/src/components/Nav/NavLinks.jsx index 33f0e453c7..fd5249e868 100644 --- a/client/src/components/Nav/NavLinks.jsx +++ b/client/src/components/Nav/NavLinks.jsx @@ -1,17 +1,14 @@ -import React from 'react'; import SearchBar from './SearchBar'; import ClearConvos from './ClearConvos'; import DarkMode from './DarkMode'; import Logout from './Logout'; import ExportConversation from './ExportConversation'; -export default function NavLinks({ fetch, onSearchSuccess, clearSearch, isSearchEnabled }) { +export default function NavLinks({ clearSearch, isSearchEnabled }) { return ( <> {!!isSearchEnabled && ( )} diff --git a/client/src/components/Nav/SearchBar.jsx b/client/src/components/Nav/SearchBar.jsx index 0d3befc762..35daa6de05 100644 --- a/client/src/components/Nav/SearchBar.jsx +++ b/client/src/components/Nav/SearchBar.jsx @@ -1,66 +1,29 @@ -import React, { useCallback, useEffect, useState } from 'react'; -import { debounce } from 'lodash'; import { Search } from 'lucide-react'; import { useRecoilState } from 'recoil'; - import store from '~/store'; -export default function SearchBar({ fetch, clearSearch }) { - // const dispatch = useDispatch(); - const [inputValue, setInputValue] = useState(''); +export default function SearchBar({ clearSearch }) { + const [searchQuery, setSearchQuery] = useRecoilState(store.searchQuery); - // const [inputValue, setInputValue] = useState(''); - - const debouncedChangeHandler = useCallback( - debounce(q => { - setSearchQuery(q); - }, 750), - [setSearchQuery] - ); - - useEffect(() => { - if (searchQuery.length > 0) { - fetch(searchQuery, 1); - setInputValue(searchQuery); - } - }, [searchQuery]); - const handleKeyUp = e => { const { value } = e.target; if (e.keyCode === 8 && value === '') { - // Value after clearing input: "" - console.log(`Value after clearing input: "${value}"`); setSearchQuery(''); clearSearch(); } }; - const changeHandler = e => { - let q = e.target.value; - setInputValue(q); - q = q.trim(); - - if (q === '') { - setSearchQuery(''); - clearSearch(); - } else { - debouncedChangeHandler(q); - } - }; - return (
{} setSearchQuery(e.target.value)} placeholder="Search messages" onKeyUp={handleKeyUp} - // onBlur={onRename} />
); diff --git a/client/src/components/Nav/index.jsx b/client/src/components/Nav/index.jsx index aa6a7895e1..b63cf11e7e 100644 --- a/client/src/components/Nav/index.jsx +++ b/client/src/components/Nav/index.jsx @@ -1,13 +1,12 @@ -import React, { useState, useEffect, useRef, useCallback } from 'react'; -import _ from 'lodash'; +import { useState, useEffect, useRef } from 'react'; import NewChat from './NewChat'; import Spinner from '../svg/Spinner'; import Pages from '../Conversations/Pages'; import Conversations from '../Conversations'; import NavLinks from './NavLinks'; -import { searchFetcher, swr } from '~/utils/fetchers'; -import { useRecoilState, useRecoilValue, useSetRecoilState } from 'recoil'; - +import { useRecoilValue, useSetRecoilState } from 'recoil'; +import { useGetConversationsQuery, useSearchQuery } from '~/data-provider'; +import useDebounce from '~/hooks/useDebounce'; import store from '~/store'; export default function Nav({ navVisible, setNavVisible }) { @@ -16,12 +15,14 @@ export default function Nav({ navVisible, setNavVisible }) { const containerRef = useRef(null); const scrollPositionRef = useRef(null); - // const dispatch = useDispatch(); const [conversations, setConversations] = useState([]); // current page const [pageNumber, setPageNumber] = useState(1); // total pages const [pages, setPages] = useState(1); + + // data provider + const getConversationsQuery = useGetConversationsQuery(pageNumber); // search const searchQuery = useRecoilValue(store.searchQuery); @@ -33,29 +34,18 @@ export default function Nav({ navVisible, setNavVisible }) { const conversation = useRecoilValue(store.conversation); const { conversationId } = conversation || {}; const setSearchResultMessages = useSetRecoilState(store.searchResultMessages); - - // refreshConversationsHint is used for other components to ask refresh of Nav const refreshConversationsHint = useRecoilValue(store.refreshConversationsHint); - const { refreshConversations } = store.useConversations(); const [isFetching, setIsFetching] = useState(false); - const onSuccess = (data, searchFetch = false) => { - if (isSearching) { - return; - } - - let { conversations, pages } = data; - if (pageNumber > pages) { - setPageNumber(pages); - } else { - if (!searchFetch) - conversations = conversations.sort((a, b) => new Date(b.createdAt) - new Date(a.createdAt)); - setConversations(conversations); - setPages(pages); - } - }; + const debouncedSearchTerm = useDebounce(searchQuery, 750); + const searchQueryFn = useSearchQuery(debouncedSearchTerm, pageNumber, { + enabled: !!debouncedSearchTerm && + debouncedSearchTerm.length > 0 && + isSearchEnabled && + isSearching, + }); const onSearchSuccess = (data, expectedPage) => { const res = data; @@ -63,21 +53,21 @@ export default function Nav({ navVisible, setNavVisible }) { if (expectedPage) { setPageNumber(expectedPage); } - setPageNumber(res.pageNumber); setPages(res.pages); setIsFetching(false); searchPlaceholderConversation(); setSearchResultMessages(res.messages); }; - // TODO: dont need this - const fetch = useCallback( - _.partialRight( - searchFetcher.bind(null, () => setIsFetching(true)), - onSearchSuccess - ), - [setIsFetching] - ); + useEffect(() => { + //we use isInitialLoading here instead of isLoading because query is disabled by default + if (searchQueryFn.isInitialLoading) { + setIsFetching(true); + } + else if (searchQueryFn.data) { + onSearchSuccess(searchQueryFn.data); + } + }, [searchQueryFn.data, searchQueryFn.isInitialLoading]) const clearSearch = () => { setPageNumber(1); @@ -85,38 +75,39 @@ export default function Nav({ navVisible, setNavVisible }) { if (conversationId == 'search') { newConversation(); } - // dispatch(setDisabled(false)); }; - const { data, isLoading, mutate } = swr(`/api/convos?pageNumber=${pageNumber}`, onSuccess, { - revalidateOnMount: false - }); - const nextPage = async () => { moveToTop(); - - if (!isSearching) { - setPageNumber(prev => prev + 1); - await mutate(); - } else { - await fetch(searchQuery, +pageNumber + 1); - } + setPageNumber(pageNumber + 1); }; const previousPage = async () => { moveToTop(); - - if (!isSearching) { - setPageNumber(prev => prev - 1); - await mutate(); - } else { - await fetch(searchQuery, +pageNumber - 1); - } + setPageNumber(pageNumber - 1); }; + useEffect(() => { + if (getConversationsQuery.data) { + if (isSearching) { + return; + } + let { conversations, pages } = getConversationsQuery.data; + if (pageNumber > pages) { + setPageNumber(pages); + } else { + if (!isSearching) { + conversations = conversations.sort((a, b) => new Date(b.createdAt) - new Date(a.createdAt)); + } + setConversations(conversations); + setPages(pages); + } + } + }, [getConversationsQuery.isSuccess, getConversationsQuery.data, isSearching, pageNumber]); + useEffect(() => { if (!isSearching) { - mutate(); + getConversationsQuery.refetch(); } }, [pageNumber, conversationId, refreshConversationsHint]); @@ -127,31 +118,17 @@ export default function Nav({ navVisible, setNavVisible }) { } }; - const moveTo = () => { - const container = containerRef.current; - - if (container && scrollPositionRef.current !== null) { - const { scrollHeight, clientHeight } = container; - const maxScrollTop = scrollHeight - clientHeight; - - container.scrollTop = Math.min(maxScrollTop, scrollPositionRef.current); - } - }; const toggleNavVisible = () => { setNavVisible(prev => !prev); }; - // useEffect(() => { - // moveTo(); - // }, [data]); - useEffect(() => { setNavVisible(false); - }, [conversationId]); + }, [conversationId, setNavVisible]); const containerClasses = - isLoading && pageNumber === 1 + getConversationsQuery.isLoading && pageNumber === 1 ? 'flex flex-col gap-2 text-gray-100 text-sm h-full justify-center items-center' : 'flex flex-col gap-2 text-gray-100 text-sm'; @@ -176,8 +153,7 @@ export default function Nav({ navVisible, setNavVisible }) { ref={containerRef} >
- {/* {(isLoading && pageNumber === 1) ? ( */} - {(isLoading && pageNumber === 1) || isFetching ? ( + {(getConversationsQuery.isLoading && pageNumber === 1) || isFetching ? ( ) : (
diff --git a/client/src/components/ui/SelectDropDown.jsx b/client/src/components/ui/SelectDropDown.jsx index d2e2e15f58..3ea1dd1170 100644 --- a/client/src/components/ui/SelectDropDown.jsx +++ b/client/src/components/ui/SelectDropDown.jsx @@ -3,7 +3,7 @@ import CheckMark from '../svg/CheckMark.jsx'; import { Listbox, Transition } from '@headlessui/react'; import { cn } from '~/utils/'; -function SelectDropdown({ +function SelectDropDown({ title = 'Model', value, disabled, @@ -111,4 +111,4 @@ function SelectDropdown({ ); } -export default SelectDropdown; +export default SelectDropDown; diff --git a/client/src/data-provider/api-endpoints.ts b/client/src/data-provider/api-endpoints.ts new file mode 100644 index 0000000000..74568e55a6 --- /dev/null +++ b/client/src/data-provider/api-endpoints.ts @@ -0,0 +1,47 @@ +export const user = () => { + return `/api/me`; +}; + +export const messages = (id: string) => { + return `/api/messages/${id}`; +}; + +export const conversations = (pageNumber: string) => { + return `/api/convos?pageNumber=${pageNumber}`; +}; + +export const conversationById = (id: string) => { + return `/api/convos/${id}`; +}; + +export const updateConversation = () => { + return `/api/convos/update`; +}; + +export const deleteConversation = () => { + return `/api/convos/clear`; +}; + +export const search = (q: string, pageNumber: string) => { + return `/api/search?q=${q}&pageNumber=${pageNumber}`; +} + +export const searchEnabled = () => { + return `/api/search/enable`; +} + +export const presets = () => { + return `/api/presets`; +} + +export const deletePreset = () => { + return `/api/presets/delete`; +} + +export const aiEndpoints = () => { + return `/api/endpoints`; +} + +export const tokenizer = () => { + return `/api/tokenizer`; +} diff --git a/client/src/utils/createPayload.js b/client/src/data-provider/createPayload.ts similarity index 81% rename from client/src/utils/createPayload.js rename to client/src/data-provider/createPayload.ts index 3035663fef..5c60e8f197 100644 --- a/client/src/utils/createPayload.js +++ b/client/src/data-provider/createPayload.ts @@ -1,4 +1,6 @@ -export default function createPayload(submission) { +import type { TSubmission } from './types'; + +export default function createPayload(submission: TSubmission) { const { conversation, message, endpointOption } = submission; const { conversationId } = conversation; const { endpoint } = endpointOption; diff --git a/client/src/data-provider/data-service.ts b/client/src/data-provider/data-service.ts new file mode 100644 index 0000000000..1b717dc888 --- /dev/null +++ b/client/src/data-provider/data-service.ts @@ -0,0 +1,66 @@ +import * as t from './types'; +import request from './request'; +import * as endpoints from './api-endpoints'; + +export function getConversations(pageNumber: string): Promise { + return request.get(endpoints.conversations(pageNumber)); +} + +export function deleteConversation(payload: t.TDeleteConversationRequest) { + //todo: this should be a DELETE request + return request.post(endpoints.deleteConversation(), {arg: payload}); +} + +export function clearAllConversations(): Promise { + return request.post(endpoints.deleteConversation(), {arg: {}}); +} + +export function getMessagesByConvoId(id: string): Promise { + return request.get(endpoints.messages(id)); +} + +export function getConversationById(id: string): Promise { + return request.get(endpoints.conversationById(id)); +} + +export function updateConversation( + payload: t.TUpdateConversationRequest +): Promise { + return request.post(endpoints.updateConversation(), {arg: payload}); +} + +export function getPresets(): Promise { + return request.get(endpoints.presets()); +} + +export function createPreset(payload: t.TPreset): Promise { + return request.post(endpoints.presets(), payload); +} + +export function updatePreset(payload: t.TPreset): Promise { + return request.post(endpoints.presets(), payload); +} + +export function deletePreset(arg: t.TPreset | {}): Promise { + return request.post(endpoints.deletePreset(), arg); +} + +export function getSearchEnabled(): Promise { + return request.get(endpoints.searchEnabled()); +} + +export function getUser(): Promise { + return request.get(endpoints.user()); +} + +export const searchConversations = async(q: string, pageNumber: string): Promise => { + return request.get(endpoints.search(q, pageNumber)); +} + +export const getAIEndpoints = () => { + return request.get(endpoints.aiEndpoints()); +} + +export const updateTokenCount = (text: string) => { + return request.post(endpoints.tokenizer(), {arg: {text}}); +} \ No newline at end of file diff --git a/client/src/data-provider/headers-helpers.ts b/client/src/data-provider/headers-helpers.ts new file mode 100644 index 0000000000..195f7c7912 --- /dev/null +++ b/client/src/data-provider/headers-helpers.ts @@ -0,0 +1,9 @@ +import axios from 'axios'; + +export function setAcceptLanguageHeader(value: string): void { + axios.defaults.headers.common['Accept-Language'] = value; +} + +export function setTokenHeader(token: string) { + axios.defaults.headers.common['Authorization'] = 'Bearer ' + token; +} diff --git a/client/src/data-provider/index.ts b/client/src/data-provider/index.ts new file mode 100644 index 0000000000..ea66903770 --- /dev/null +++ b/client/src/data-provider/index.ts @@ -0,0 +1,7 @@ +export * from './data-service'; +// export * from './endpoints'; +export * from './request'; +export * from './types'; +export * from './react-query-service'; +export * from './headers-helpers'; +// export * from './events'; \ No newline at end of file diff --git a/client/src/data-provider/react-query-service.ts b/client/src/data-provider/react-query-service.ts new file mode 100644 index 0000000000..c85660214c --- /dev/null +++ b/client/src/data-provider/react-query-service.ts @@ -0,0 +1,224 @@ +import { + UseQueryOptions, + useQuery, + useMutation, + useQueryClient, + UseMutationResult, + QueryObserverResult, +} from "@tanstack/react-query"; +import * as t from "./types"; +import * as dataService from "./data-service"; + +export enum QueryKeys { + messages = "messsages", + allConversations = "allConversations", + conversation = "conversation", + searchEnabled = "searchEnabled", + user = "user", + endpoints = "endpoints", + presets = "presets", + searchResults = "searchResults", + tokenCount = "tokenCount", +} + +export const useGetUserQuery = (): QueryObserverResult => { + return useQuery([QueryKeys.user], () => dataService.getUser(), { + refetchOnWindowFocus: false, + refetchOnReconnect: false, + refetchOnMount: false, + }); +}; + +export const useGetMessagesByConvoId = ( + id: string, + config?: UseQueryOptions +): QueryObserverResult => { + return useQuery([QueryKeys.messages, id], () => + dataService.getMessagesByConvoId(id), + { + refetchOnWindowFocus: false, + refetchOnReconnect: false, + refetchOnMount: false, + ...config, + } + ); +}; + +export const useGetConversationByIdQuery = ( + id: string, + config?: UseQueryOptions + ): QueryObserverResult => { + return useQuery([QueryKeys.conversation, id], () => + dataService.getConversationById(id), + { + refetchOnWindowFocus: false, + refetchOnReconnect: false, + refetchOnMount: false, + ...config + } + ); +} + +//This isn't ideal because its just a query and we're using mutation, but it was the only way +//to make it work with how the Chat component is structured +export const useGetConversationByIdMutation = ( + id: string, + callback: (data: t.TConversation) => void +): UseMutationResult => { + const queryClient = useQueryClient(); + return useMutation(() => dataService.getConversationById(id), + { + onSuccess: (res: t.TConversation) => { + callback(res); + queryClient.invalidateQueries([QueryKeys.conversation, id]); + }, + } + ); +}; + +export const useUpdateConversationMutation = ( + id: string +): UseMutationResult => { + const queryClient = useQueryClient(); + return useMutation( + (payload: t.TUpdateConversationRequest) => + dataService.updateConversation(payload), + { + onSuccess: () => { + queryClient.invalidateQueries([QueryKeys.conversation, id]); + queryClient.invalidateQueries([QueryKeys.allConversations]); + }, + } + ); +}; + + +export const useDeleteConversationMutation = ( + id?: string +): UseMutationResult => { + const queryClient = useQueryClient(); + return useMutation( + (payload: t.TDeleteConversationRequest) => + dataService.deleteConversation(payload), + { + onSuccess: () => { + queryClient.invalidateQueries([QueryKeys.conversation, id]); + queryClient.invalidateQueries([QueryKeys.allConversations]); + }, + } + ); +}; + +export const useClearConversationsMutation = (): UseMutationResult => { + const queryClient = useQueryClient(); + return useMutation(() => dataService.clearAllConversations(), { + onSuccess: () => { + queryClient.invalidateQueries([QueryKeys.allConversations]); + }, + }); +}; + +export const useGetConversationsQuery = (pageNumber: string): QueryObserverResult => { + return useQuery([QueryKeys.allConversations, pageNumber], () => + dataService.getConversations(pageNumber), { + refetchOnReconnect: false, + refetchOnMount: false, + } + ); +} + +export const useGetSearchEnabledQuery = (config?: UseQueryOptions): QueryObserverResult => { + return useQuery([QueryKeys.searchEnabled], () => + dataService.getSearchEnabled(), { + refetchOnWindowFocus: false, + refetchOnReconnect: false, + refetchOnMount: false, + ...config, + } + ); +} + +export const useGetEndpointsQuery = (): QueryObserverResult => { + return useQuery([QueryKeys.endpoints], () => + dataService.getAIEndpoints(), { + refetchOnWindowFocus: false, + refetchOnReconnect: false, + refetchOnMount: false, + } + ); +} + +export const useCreatePresetMutation = (): UseMutationResult => { + const queryClient = useQueryClient(); + return useMutation( + (payload: t.TPreset) => + dataService.createPreset(payload), + { + onSuccess: () => { + queryClient.invalidateQueries([QueryKeys.presets]); + }, + } + ); +}; + +export const useUpdatePresetMutation = (): UseMutationResult => { + const queryClient = useQueryClient(); + return useMutation( + (payload: t.TPreset) => + dataService.updatePreset(payload), + { + onSuccess: () => { + queryClient.invalidateQueries([QueryKeys.presets]); + }, + } + ); +}; + +export const useGetPresetsQuery = (): QueryObserverResult => { + return useQuery([QueryKeys.presets], () => dataService.getPresets(), { + refetchOnWindowFocus: false, + refetchOnReconnect: false, + refetchOnMount: false, + }); +}; + +export const useDeletePresetMutation = (): UseMutationResult => { + const queryClient = useQueryClient(); + return useMutation( + (payload: t.TPreset | {}) => + dataService.deletePreset(payload), + { + onSuccess: () => { + queryClient.invalidateQueries([QueryKeys.presets]); + }, + } + ); +} + +export const useSearchQuery = ( + searchQuery: string, + pageNumber: string, + config?: UseQueryOptions + ): QueryObserverResult => { + return useQuery([QueryKeys.searchResults, pageNumber, searchQuery], () => + dataService.searchConversations(searchQuery, pageNumber), { + refetchOnWindowFocus: false, + refetchOnReconnect: false, + refetchOnMount: false, + ...config + } + ); +} + +export const useUpdateTokenCountMutation = (): UseMutationResult => { + const queryClient = useQueryClient(); + return useMutation( + (text: string) => + dataService.updateTokenCount(text), + { + onSuccess: () => { + queryClient.invalidateQueries([QueryKeys.tokenCount]); + }, + } + ); +} \ No newline at end of file diff --git a/client/src/data-provider/request.ts b/client/src/data-provider/request.ts new file mode 100644 index 0000000000..d79562ab34 --- /dev/null +++ b/client/src/data-provider/request.ts @@ -0,0 +1,62 @@ +import axios, { AxiosRequestConfig } from "axios"; + +async function _get(url: string, options?: AxiosRequestConfig): Promise { + const response = await axios.get(url, { withCredentials: true, ...options}); + return response.data; +} + +async function _post(url: string, data?: any) { + const response = await axios.post(url, JSON.stringify(data), { + headers: { "Content-Type": "application/json" }, + }); + return response.data; +} + +async function _postMultiPart( + url: string, + formData: FormData, + options?: AxiosRequestConfig +) { + const response = await axios.post(url, formData, { + ...options, + headers: { "Content-Type": "multipart/form-data" }, + }); + return response.data; +} + +async function _put(url: string, data?: any) { + const response = await axios.put(url, JSON.stringify(data), { + headers: { "Content-Type": "application/json" }, + }); + return response.data; +} + +async function _delete(url: string): Promise { + const response = await axios.delete(url); + return response.data; +} + +async function _deleteWithOptions( + url: string, + options?: AxiosRequestConfig +): Promise { + const response = await axios.delete(url, {...options}); + return response.data; +} + +async function _patch(url: string, data?: any) { + const response = await axios.patch(url, JSON.stringify(data), { + headers: { "Content-Type": "application/json" }, + }); + return response.data; +} + +export default { + get: _get, + post: _post, + postMultiPart: _postMultiPart, + put: _put, + delete: _delete, + deleteWithOptions: _deleteWithOptions, + patch: _patch, +}; diff --git a/client/src/utils/sse.mjs b/client/src/data-provider/sse.mjs similarity index 100% rename from client/src/utils/sse.mjs rename to client/src/data-provider/sse.mjs diff --git a/client/src/data-provider/types.ts b/client/src/data-provider/types.ts new file mode 100644 index 0000000000..b3206fce64 --- /dev/null +++ b/client/src/data-provider/types.ts @@ -0,0 +1,163 @@ +export type TMessage = { + messageId: string, + conversationId: string, + clientId: string, + parentMessageId: string, + sender: string, + text: string, + isCreatedByUser: boolean, + error: boolean, + createdAt: string, + updatedAt: string, +}; + +export type TSubmission = { + clientId?: string; + context?: string; + conversationId?: string; + conversationSignature?: string; + current: boolean; + endpoint: EModelEndpoint; + invocationId: number; + isCreatedByUser: boolean; + jailbreak: boolean; + jailbreakConversationId?: string; + messageId: string; + overrideParentMessageId?: string | boolean; + parentMessageId?: string; + sender: string; + systemMessage?: string; + text: string; + toneStyle?: string; + model?: string; + promptPrefix?: string; + temperature?: number; + top_p?: number; + presence_penalty?: number; + frequence_penalty?: number; +} + + +export enum EModelEndpoint { + azureOpenAI = 'azureOpenAI', + openAI = 'openAI', + bingAI = 'bingAI', + chatGPT = 'chatGPT', + chatGPTBrowser = 'chatGPTBrowser' +} + +export type TConversation = { + conversationId: string; + title: string; + user?: string; + endpoint: EModelEndpoint; + suggestions?: string[]; + messages?: TMessage[]; + createdAt: string; + updatedAt: string; + // for azureOpenAI, openAI only + chatGptLabel?: string; + model?: string; + promptPrefix?: string; + temperature?: number; + top_p?: number; + presence_penalty?: number; + // for bingAI only + jailbreak?: boolean; + jailbreakConversationId?: string; + conversationSignature?: string; + parentMessageId?: string; + clientId?: string; + invocationId?: string; + toneStyle?: string; +} + +export type TPreset = { + title: string, + endpoint: EModelEndpoint, + conversationSignature?: string, + createdAt?: string, + updatedAt?: string, + presetId?: string, + user?: string, + // for azureOpenAI, openAI only + chatGptLabel?: string, + frequence_penalty?: number, + model?: string, + presence_penalty?: number, + promptPrefix?: string, + temperature?: number, + top_p?: number, + //for BingAI + clientId?: string, + invocationId?: number, + jailbreak?: boolean, + jailbreakPresetId?: string, + presetSignature?: string, + toneStyle?: string, +} + +export type TUser = { + username: string, + display: string +}; + +export type TGetConversationsResponse = { + conversations: TConversation[], + pageNumber: string, + pageSize: string | number, + pages: string | number +}; + +export type TUpdateConversationRequest = { + conversationId: string, + title: string, +}; + +export type TUpdateConversationResponse = { + data: TConversation +}; + +export type TDeleteConversationRequest = { + conversationId?: string, + source?: string +} + +export type TDeleteConversationResponse = { + acknowledged: boolean, + deletedCount: number, + messages: { + acknowledged: boolean, + deletedCount: number + } +}; + +export type TSearchResults = { + conversations: TConversation[], + messages: TMessage[], + pageNumber: string, + pageSize: string | number, + pages: string | number + filter: {} +}; + +export type TEndpoints = { + azureOpenAI: boolean, + bingAI: boolean, + ChatGptBrowser: { + availableModels: [] + } + OpenAI: { + availableModels: [] + } +}; + +export type TUpdateTokenCountResponse = { + count: number, +}; + +export type TMessageTreeNode = {} + +export type TSearchMessage = {} + +export type TSearchMessageTreeNode = {} \ No newline at end of file diff --git a/client/src/hooks/useDebounce.js b/client/src/hooks/useDebounce.js new file mode 100644 index 0000000000..910395fce6 --- /dev/null +++ b/client/src/hooks/useDebounce.js @@ -0,0 +1,22 @@ +import { useState, useEffect } from 'react'; + +function useDebounce(value, delay) { + const [debouncedValue, setDebouncedValue] = useState(value); + + useEffect( + () => { + const handler = setTimeout(() => { + setDebouncedValue(value); + }, delay); + + return () => { + clearTimeout(handler); + }; + }, + [value, delay] + ); + + return debouncedValue; +} + +export default useDebounce; \ No newline at end of file diff --git a/client/src/main.jsx b/client/src/main.jsx index 8738dcf27f..b27ce94477 100644 --- a/client/src/main.jsx +++ b/client/src/main.jsx @@ -1,9 +1,6 @@ -import React from 'react'; import { createRoot } from 'react-dom/client'; -// import { Provider } from 'react-redux'; -// import { store } from './src/store'; import { RecoilRoot } from 'recoil'; - +import { QueryClient, QueryClientProvider } from "@tanstack/react-query"; import { ThemeProvider } from './hooks/ThemeContext'; import App from './App'; import './style.css'; @@ -12,10 +9,14 @@ import './mobile.css'; const container = document.getElementById('root'); const root = createRoot(container); +const queryClient = new QueryClient(); + root.render( - - - - - + + + + + + + ); diff --git a/client/src/routes/Chat.jsx b/client/src/routes/Chat.jsx index 3feab50a0f..88202b8221 100644 --- a/client/src/routes/Chat.jsx +++ b/client/src/routes/Chat.jsx @@ -1,4 +1,4 @@ -import React, { useEffect } from 'react'; +import { useEffect } from 'react'; import { useNavigate, useParams } from 'react-router-dom'; import { useRecoilState, useRecoilValue, useSetRecoilState } from 'recoil'; @@ -7,7 +7,7 @@ import Messages from '../components/Messages'; import TextChat from '../components/Input'; import store from '~/store'; -import manualSWR from '~/utils/fetchers'; +import { useGetMessagesByConvoId, useGetConversationByIdMutation } from '~/data-provider'; export default function Chat() { const searchQuery = useRecoilValue(store.searchQuery); @@ -18,9 +18,9 @@ export default function Chat() { const { conversationId } = useParams(); const navigate = useNavigate(); - const { trigger: messagesTrigger } = manualSWR(`/api/messages/${conversation?.conversationId}`, 'get'); - - const { trigger: conversationTrigger } = manualSWR(`/api/convos/${conversationId}`, 'get'); + //disabled by default, we only enable it when messagesTree is null + const messagesQuery = useGetMessagesByConvoId(conversationId, { enabled: false }); + const getConversationMutation = useGetConversationByIdMutation(conversationId, setConversation); // when conversation changed or conversationId (in url) changed useEffect(() => { @@ -31,13 +31,7 @@ export default function Chat() { newConversation(); } else if (conversationId) { // fetch it from server - conversationTrigger() - .then(setConversation) - .catch(error => { - console.error('failed to fetch the conversation'); - console.error(error); - newConversation(); - }); + getConversationMutation.mutate(); setMessages(null); } else { navigate(`/chat/new`); @@ -51,13 +45,30 @@ export default function Chat() { } }, [conversation, conversationId]); - // when messagesTree is null (<=> messages is null) - // we need to fetch message list from server useEffect(() => { - if (messagesTree === null) { - messagesTrigger().then(setMessages); + if(getConversationMutation.isError) { + console.error('failed to fetch the conversation'); + console.error(getConversationMutation.error); + newConversation(); } - }, [conversation?.conversationId]); + }, [getConversationMutation.isError, newConversation]); + + + useEffect(() => { + if (messagesTree === null && conversation?.conversationId) { + messagesQuery.refetch(conversation?.conversationId); + } + }, [conversation?.conversationId, messagesQuery, messagesTree]); + + useEffect(() => { + if (messagesQuery.data) { + setMessages(messagesQuery.data); + } else if(messagesQuery.isError) { + console.error('failed to fetch the messages'); + console.error(messagesQuery.error); + setMessages(null); + } + }, [messagesQuery.data, messagesQuery.isError, setMessages]); // if not a conversation if (conversation?.conversationId === 'search') return null; diff --git a/client/src/utils/fetchers.js b/client/src/utils/fetchers.js deleted file mode 100644 index a3a693b8fe..0000000000 --- a/client/src/utils/fetchers.js +++ /dev/null @@ -1,96 +0,0 @@ -/* eslint-disable react-hooks/rules-of-hooks */ -import axios from 'axios'; -import useSWR from 'swr'; -import useSWRMutation from 'swr/mutation'; - -const fetcher = url => fetch(url, { credentials: 'include' }).then(res => res.json()); -const axiosFetcher = async (url, params) => { - console.log(params, 'params'); - return axios.get(url, params); -}; - -export const postRequest = async (url, { arg }) => { - return await axios({ - method: 'post', - url: url, - withCredentials: true, - data: { arg } - }); -}; - -export const axiosPost = async ({ url, arg, callback }) => { - try { - const response = await axios.post(url, { arg }, { withCredentials: true }); - callback(response.data); - } catch (error) { - console.error('An error occurred while making the axios post request:', error); - } -}; - -export const searchFetcher = async (pre, q, pageNumber, callback) => { - pre(); - const { data } = await axios.get(`/api/search?q=${q}&pageNumber=${pageNumber}`); - console.log('search data', data); - callback(data); -}; - -export const fetchById = async (path, conversationId) => { - return await axios.get(`/api/${path}/${conversationId}`); - // console.log(`fetch ${path} data`, data); - // callback(data); -}; - -export const swr = (path, successCallback, options) => { - const _options = { ...options }; - - if (successCallback) { - _options.onSuccess = successCallback; - } - - return useSWR(path, fetcher, _options); -}; - -export default function manualSWR(path, type, successCallback) { - const options = {}; - - if (successCallback) { - options.onSuccess = successCallback; - } - const fetchFunction = type === 'get' ? fetcher : postRequest; - return useSWRMutation(path, fetchFunction, options); -} - -export function useManualSWR({ path, params, type, onSuccess }) { - const options = {}; - - if (onSuccess) { - options.onSuccess = onSuccess; - } - - console.log(params, 'params'); - - const fetchFunction = type === 'get' ? _.partialRight(axiosFetcher, params) : postRequest; - return useSWRMutation(path, fetchFunction, options); -} - -export const handleFileSelected = async jsonData => { - try { - const response = await axios({ - url: '/api/presets', - method: 'POST', - headers: { - 'Content-Type': 'application/json' - }, - withCredentials: true, - data: JSON.stringify(jsonData) - }); - - // if (!response.ok) { - // throw new Error(`Error: ${response.statusText}`); - // } - console.log(response); - return response.data; - } catch (error) { - console.error('Error uploading the preset:', error); - } -};