From 93ca48bb5d9d33395460168e8b7868e763250eb8 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 30 Aug 2024 22:36:11 +0000 Subject: [PATCH 1/6] Bump webpack in the npm_and_yarn group across 1 directory Bumps the npm_and_yarn group with 1 update in the / directory: [webpack](https://github.com/webpack/webpack). Updates `webpack` from 5.89.0 to 5.94.0 - [Release notes](https://github.com/webpack/webpack/releases) - [Commits](https://github.com/webpack/webpack/compare/v5.89.0...v5.94.0) --- updated-dependencies: - dependency-name: webpack dependency-type: indirect dependency-group: npm_and_yarn ... Signed-off-by: dependabot[bot] --- package-lock.json | 223 +++++++++++++++++++++------------------------- 1 file changed, 101 insertions(+), 122 deletions(-) diff --git a/package-lock.json b/package-lock.json index b392defd..6055d78d 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1839,26 +1839,6 @@ "@types/ms": "*" } }, - "node_modules/@types/eslint": { - "version": "8.44.8", - "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-8.44.8.tgz", - "integrity": "sha512-4K8GavROwhrYl2QXDXm0Rv9epkA8GBFu0EI+XrrnnuCl7u8CWBRusX7fXJfanhZTDWSAL24gDI/UqXyUM0Injw==", - "peer": true, - "dependencies": { - "@types/estree": "*", - "@types/json-schema": "*" - } - }, - "node_modules/@types/eslint-scope": { - "version": "3.7.7", - "resolved": "https://registry.npmjs.org/@types/eslint-scope/-/eslint-scope-3.7.7.tgz", - "integrity": "sha512-MzMFlSLBqNF2gcHWO0G1vP/YQyfvrxZ0bF+u7mzUdZ1/xK4A4sru+nraZz5i3iEIk1l1uyicaDVTB4QbbEkAYg==", - "peer": true, - "dependencies": { - "@types/eslint": "*", - "@types/estree": "*" - } - }, "node_modules/@types/estree": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.5.tgz", @@ -2364,9 +2344,9 @@ "dev": true }, "node_modules/@webassemblyjs/ast": { - "version": "1.11.6", - "resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.11.6.tgz", - "integrity": "sha512-IN1xI7PwOvLPgjcf180gC1bqn3q/QaOCwYUahIOhbYUu8KA/3tw2RT/T0Gidi1l7Hhj5D/INhJxiICObqpMu4Q==", + "version": "1.12.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.12.1.tgz", + "integrity": "sha512-EKfMUOPRRUTy5UII4qJDGPpqfwjOmZ5jeGFwid9mnoqIFK+e0vqoi1qH56JpmZSzEL53jKnNzScdmftJyG5xWg==", "peer": true, "dependencies": { "@webassemblyjs/helper-numbers": "1.11.6", @@ -2386,9 +2366,9 @@ "peer": true }, "node_modules/@webassemblyjs/helper-buffer": { - "version": "1.11.6", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-buffer/-/helper-buffer-1.11.6.tgz", - "integrity": "sha512-z3nFzdcp1mb8nEOFFk8DrYLpHvhKC3grJD2ardfKOzmbmJvEf/tPIqCY+sNcwZIY8ZD7IkB2l7/pqhUhqm7hLA==", + "version": "1.12.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-buffer/-/helper-buffer-1.12.1.tgz", + "integrity": "sha512-nzJwQw99DNDKr9BVCOZcLuJJUlqkJh+kVzVl6Fmq/tI5ZtEyWT1KZMyOXltXLZJmDtvLCDgwsyrkohEtopTXCw==", "peer": true }, "node_modules/@webassemblyjs/helper-numbers": { @@ -2409,15 +2389,15 @@ "peer": true }, "node_modules/@webassemblyjs/helper-wasm-section": { - "version": "1.11.6", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.11.6.tgz", - "integrity": "sha512-LPpZbSOwTpEC2cgn4hTydySy1Ke+XEu+ETXuoyvuyezHO3Kjdu90KK95Sh9xTbmjrCsUwvWwCOQQNta37VrS9g==", + "version": "1.12.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.12.1.tgz", + "integrity": "sha512-Jif4vfB6FJlUlSbgEMHUyk1j234GTNG9dBJ4XJdOySoj518Xj0oGsNi59cUQF4RRMS9ouBUxDDdyBVfPTypa5g==", "peer": true, "dependencies": { - "@webassemblyjs/ast": "1.11.6", - "@webassemblyjs/helper-buffer": "1.11.6", + "@webassemblyjs/ast": "1.12.1", + "@webassemblyjs/helper-buffer": "1.12.1", "@webassemblyjs/helper-wasm-bytecode": "1.11.6", - "@webassemblyjs/wasm-gen": "1.11.6" + "@webassemblyjs/wasm-gen": "1.12.1" } }, "node_modules/@webassemblyjs/ieee754": { @@ -2445,28 +2425,28 @@ "peer": true }, "node_modules/@webassemblyjs/wasm-edit": { - "version": "1.11.6", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-edit/-/wasm-edit-1.11.6.tgz", - "integrity": "sha512-Ybn2I6fnfIGuCR+Faaz7YcvtBKxvoLV3Lebn1tM4o/IAJzmi9AWYIPWpyBfU8cC+JxAO57bk4+zdsTjJR+VTOw==", + "version": "1.12.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-edit/-/wasm-edit-1.12.1.tgz", + "integrity": "sha512-1DuwbVvADvS5mGnXbE+c9NfA8QRcZ6iKquqjjmR10k6o+zzsRVesil54DKexiowcFCPdr/Q0qaMgB01+SQ1u6g==", "peer": true, "dependencies": { - "@webassemblyjs/ast": "1.11.6", - "@webassemblyjs/helper-buffer": "1.11.6", + "@webassemblyjs/ast": "1.12.1", + "@webassemblyjs/helper-buffer": "1.12.1", "@webassemblyjs/helper-wasm-bytecode": "1.11.6", - "@webassemblyjs/helper-wasm-section": "1.11.6", - "@webassemblyjs/wasm-gen": "1.11.6", - "@webassemblyjs/wasm-opt": "1.11.6", - "@webassemblyjs/wasm-parser": "1.11.6", - "@webassemblyjs/wast-printer": "1.11.6" + "@webassemblyjs/helper-wasm-section": "1.12.1", + "@webassemblyjs/wasm-gen": "1.12.1", + "@webassemblyjs/wasm-opt": "1.12.1", + "@webassemblyjs/wasm-parser": "1.12.1", + "@webassemblyjs/wast-printer": "1.12.1" } }, "node_modules/@webassemblyjs/wasm-gen": { - "version": "1.11.6", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-gen/-/wasm-gen-1.11.6.tgz", - "integrity": "sha512-3XOqkZP/y6B4F0PBAXvI1/bky7GryoogUtfwExeP/v7Nzwo1QLcq5oQmpKlftZLbT+ERUOAZVQjuNVak6UXjPA==", + "version": "1.12.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-gen/-/wasm-gen-1.12.1.tgz", + "integrity": "sha512-TDq4Ojh9fcohAw6OIMXqiIcTq5KUXTGRkVxbSo1hQnSy6lAM5GSdfwWeSxpAo0YzgsgF182E/U0mDNhuA0tW7w==", "peer": true, "dependencies": { - "@webassemblyjs/ast": "1.11.6", + "@webassemblyjs/ast": "1.12.1", "@webassemblyjs/helper-wasm-bytecode": "1.11.6", "@webassemblyjs/ieee754": "1.11.6", "@webassemblyjs/leb128": "1.11.6", @@ -2474,24 +2454,24 @@ } }, "node_modules/@webassemblyjs/wasm-opt": { - "version": "1.11.6", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-opt/-/wasm-opt-1.11.6.tgz", - "integrity": "sha512-cOrKuLRE7PCe6AsOVl7WasYf3wbSo4CeOk6PkrjS7g57MFfVUF9u6ysQBBODX0LdgSvQqRiGz3CXvIDKcPNy4g==", + "version": "1.12.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-opt/-/wasm-opt-1.12.1.tgz", + "integrity": "sha512-Jg99j/2gG2iaz3hijw857AVYekZe2SAskcqlWIZXjji5WStnOpVoat3gQfT/Q5tb2djnCjBtMocY/Su1GfxPBg==", "peer": true, "dependencies": { - "@webassemblyjs/ast": "1.11.6", - "@webassemblyjs/helper-buffer": "1.11.6", - "@webassemblyjs/wasm-gen": "1.11.6", - "@webassemblyjs/wasm-parser": "1.11.6" + "@webassemblyjs/ast": "1.12.1", + "@webassemblyjs/helper-buffer": "1.12.1", + "@webassemblyjs/wasm-gen": "1.12.1", + "@webassemblyjs/wasm-parser": "1.12.1" } }, "node_modules/@webassemblyjs/wasm-parser": { - "version": "1.11.6", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-parser/-/wasm-parser-1.11.6.tgz", - "integrity": "sha512-6ZwPeGzMJM3Dqp3hCsLgESxBGtT/OeCvCZ4TA1JUPYgmhAx38tTPR9JaKy0S5H3evQpO/h2uWs2j6Yc/fjkpTQ==", + "version": "1.12.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-parser/-/wasm-parser-1.12.1.tgz", + "integrity": "sha512-xikIi7c2FHXysxXe3COrVUPSheuBtpcfhbpFj4gmu7KRLYOzANztwUU0IbsqvMqzuNK2+glRGWCEqZo1WCLyAQ==", "peer": true, "dependencies": { - "@webassemblyjs/ast": "1.11.6", + "@webassemblyjs/ast": "1.12.1", "@webassemblyjs/helper-api-error": "1.11.6", "@webassemblyjs/helper-wasm-bytecode": "1.11.6", "@webassemblyjs/ieee754": "1.11.6", @@ -2500,12 +2480,12 @@ } }, "node_modules/@webassemblyjs/wast-printer": { - "version": "1.11.6", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wast-printer/-/wast-printer-1.11.6.tgz", - "integrity": "sha512-JM7AhRcE+yW2GWYaKeHL5vt4xqee5N2WcezptmgyhNS+ScggqcT1OtXykhAb13Sn5Yas0j2uv9tHgrjwvzAP4A==", + "version": "1.12.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wast-printer/-/wast-printer-1.12.1.tgz", + "integrity": "sha512-+X4WAlOisVWQMikjbcvY2e0rwPsKQ9F688lksZhBcPycBBuii3O7m8FACbDMWDojpAqvjIncrG8J0XHKyQfVeA==", "peer": true, "dependencies": { - "@webassemblyjs/ast": "1.11.6", + "@webassemblyjs/ast": "1.12.1", "@xtuc/long": "4.2.2" } }, @@ -2551,10 +2531,10 @@ "node": ">=0.4.0" } }, - "node_modules/acorn-import-assertions": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/acorn-import-assertions/-/acorn-import-assertions-1.9.0.tgz", - "integrity": "sha512-cmMwop9x+8KFhxvKrKfPYmN6/pKTYYHBqLa0DfvVZcKMJWNyWLnaqND7dx/qn66R7ewM1UX5XMaDVP5wlVTaVA==", + "node_modules/acorn-import-attributes": { + "version": "1.9.5", + "resolved": "https://registry.npmjs.org/acorn-import-attributes/-/acorn-import-attributes-1.9.5.tgz", + "integrity": "sha512-n02Vykv5uA3eHGM/Z2dQrcD56kL8TyDb2p1+0P83PClMnC/nc+anbQRhIOWnSq4Ke/KvDPrY3C9hDtC/A3eHnQ==", "peer": true, "peerDependencies": { "acorn": "^8" @@ -3879,9 +3859,9 @@ "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==" }, "node_modules/enhanced-resolve": { - "version": "5.15.0", - "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.15.0.tgz", - "integrity": "sha512-LXYT42KJ7lpIKECr2mAXIaMldcNCh/7E0KBKOu4KSfkHmP+mZmSs+8V5gBAqisWBy0OO4W5Oyys0GO1Y8KtdKg==", + "version": "5.17.1", + "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.17.1.tgz", + "integrity": "sha512-LMHl3dXhTcfv8gM4kEzIUeTQ+7fpdA0l2tUf34BddXPkz2A5xJ5L/Pchd5BL6rdccM9QGvu0sWZzK1Z1t4wwyg==", "dependencies": { "graceful-fs": "^4.2.4", "tapable": "^2.2.0" @@ -5867,6 +5847,35 @@ "reflect.getprototypeof": "^1.0.3" } }, + "node_modules/jest-worker": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-27.5.1.tgz", + "integrity": "sha512-7vuh85V5cdDofPyxn58nrPjBktZo0u9x1g8WtjQol+jZDaE+fhN+cIvTj11GndBnMnyfrUOG1sZQxCdjKh+DKg==", + "peer": true, + "dependencies": { + "@types/node": "*", + "merge-stream": "^2.0.0", + "supports-color": "^8.0.0" + }, + "engines": { + "node": ">= 10.13.0" + } + }, + "node_modules/jest-worker/node_modules/supports-color": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", + "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", + "peer": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/supports-color?sponsor=1" + } + }, "node_modules/jiti": { "version": "1.19.3", "resolved": "https://registry.npmjs.org/jiti/-/jiti-1.19.3.tgz", @@ -8420,9 +8429,9 @@ "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" }, "node_modules/serialize-javascript": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.1.tgz", - "integrity": "sha512-owoXEFjWRllis8/M1Q+Cw5k8ZH40e3zhp/ovX+Xr/vi1qj6QesbyXXViFbpNvWvPNAD62SutwEXavefrLJWj7w==", + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.2.tgz", + "integrity": "sha512-Saa1xPByTTq2gdeFZYLLo+RFE35NHZkAbqZeWNd3BpzppeVisAqpDjcp8dyf6uIvEqJRd46jemmyA4iFIeVk8g==", "peer": true, "dependencies": { "randombytes": "^2.1.0" @@ -8995,16 +9004,16 @@ } }, "node_modules/terser-webpack-plugin": { - "version": "5.3.9", - "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-5.3.9.tgz", - "integrity": "sha512-ZuXsqE07EcggTWQjXUj+Aot/OMcD0bMKGgF63f7UxYcu5/AJF53aIpK1YoP5xR9l6s/Hy2b+t1AM0bLNPRuhwA==", + "version": "5.3.10", + "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-5.3.10.tgz", + "integrity": "sha512-BKFPWlPDndPs+NGGCr1U59t0XScL5317Y0UReNrHaw9/FwhPENlq6bfgs+4yPfyP51vqC1bQ4rp1EfXW5ZSH9w==", "peer": true, "dependencies": { - "@jridgewell/trace-mapping": "^0.3.17", + "@jridgewell/trace-mapping": "^0.3.20", "jest-worker": "^27.4.5", "schema-utils": "^3.1.1", "serialize-javascript": "^6.0.1", - "terser": "^5.16.8" + "terser": "^5.26.0" }, "engines": { "node": ">= 10.13.0" @@ -9028,35 +9037,6 @@ } } }, - "node_modules/terser-webpack-plugin/node_modules/jest-worker": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-27.5.1.tgz", - "integrity": "sha512-7vuh85V5cdDofPyxn58nrPjBktZo0u9x1g8WtjQol+jZDaE+fhN+cIvTj11GndBnMnyfrUOG1sZQxCdjKh+DKg==", - "peer": true, - "dependencies": { - "@types/node": "*", - "merge-stream": "^2.0.0", - "supports-color": "^8.0.0" - }, - "engines": { - "node": ">= 10.13.0" - } - }, - "node_modules/terser-webpack-plugin/node_modules/supports-color": { - "version": "8.1.1", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", - "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", - "peer": true, - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/supports-color?sponsor=1" - } - }, "node_modules/terser/node_modules/commander": { "version": "2.20.3", "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", @@ -9962,9 +9942,9 @@ } }, "node_modules/watchpack": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.4.0.tgz", - "integrity": "sha512-Lcvm7MGST/4fup+ifyKi2hjyIAwcdI4HRgtvTpIUxBRhB+RFtUh8XtDOxUfctVCnhVi+QQj49i91OyvzkJl6cg==", + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.4.2.tgz", + "integrity": "sha512-TnbFSbcOCcDgjZ4piURLCbJ3nJhznVh9kw6F6iokjiFPl8ONxe9A6nMDVXDiNbrSfLILs6vB07F7wLBrwPYzJw==", "peer": true, "dependencies": { "glob-to-regexp": "^0.4.1", @@ -9984,34 +9964,33 @@ } }, "node_modules/webpack": { - "version": "5.89.0", - "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.89.0.tgz", - "integrity": "sha512-qyfIC10pOr70V+jkmud8tMfajraGCZMBWJtrmuBymQKCrLTRejBI8STDp1MCyZu/QTdZSeacCQYpYNQVOzX5kw==", + "version": "5.94.0", + "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.94.0.tgz", + "integrity": "sha512-KcsGn50VT+06JH/iunZJedYGUJS5FGjow8wb9c0v5n1Om8O1g4L6LjtfxwlXIATopoQu+vOXXa7gYisWxCoPyg==", "peer": true, "dependencies": { - "@types/eslint-scope": "^3.7.3", - "@types/estree": "^1.0.0", - "@webassemblyjs/ast": "^1.11.5", - "@webassemblyjs/wasm-edit": "^1.11.5", - "@webassemblyjs/wasm-parser": "^1.11.5", + "@types/estree": "^1.0.5", + "@webassemblyjs/ast": "^1.12.1", + "@webassemblyjs/wasm-edit": "^1.12.1", + "@webassemblyjs/wasm-parser": "^1.12.1", "acorn": "^8.7.1", - "acorn-import-assertions": "^1.9.0", - "browserslist": "^4.14.5", + "acorn-import-attributes": "^1.9.5", + "browserslist": "^4.21.10", "chrome-trace-event": "^1.0.2", - "enhanced-resolve": "^5.15.0", + "enhanced-resolve": "^5.17.1", "es-module-lexer": "^1.2.1", "eslint-scope": "5.1.1", "events": "^3.2.0", "glob-to-regexp": "^0.4.1", - "graceful-fs": "^4.2.9", + "graceful-fs": "^4.2.11", "json-parse-even-better-errors": "^2.3.1", "loader-runner": "^4.2.0", "mime-types": "^2.1.27", "neo-async": "^2.6.2", "schema-utils": "^3.2.0", "tapable": "^2.1.1", - "terser-webpack-plugin": "^5.3.7", - "watchpack": "^2.4.0", + "terser-webpack-plugin": "^5.3.10", + "watchpack": "^2.4.1", "webpack-sources": "^3.2.3" }, "bin": { From de7002cf64d31b851c5b64d36ccfc762a7e2b42b Mon Sep 17 00:00:00 2001 From: saidam90 Date: Mon, 2 Sep 2024 01:16:30 +0400 Subject: [PATCH 2/6] Add search functionality --- src/app/components/PairSelector.tsx | 74 ++++++++++++++++++++++++++--- 1 file changed, 67 insertions(+), 7 deletions(-) diff --git a/src/app/components/PairSelector.tsx b/src/app/components/PairSelector.tsx index 7e2381bb..f5d97d13 100644 --- a/src/app/components/PairSelector.tsx +++ b/src/app/components/PairSelector.tsx @@ -162,14 +162,74 @@ export function PairSelector() { }; }, [handleKeyDown]); + const searchPairs = (query: string, pairsList: PairInfo[]): PairInfo[] => { + const searchQuery = query.trim().toLowerCase().replace(/\s+/g, " "); + + const hasTypoTolerance = (source: string, target: string): boolean => { + const maxTyposAllowed = Math.floor(source.length / 4); + + if (source.length > target.length + maxTyposAllowed) { + return false; + } + + if (target.includes(source)) { + return true; + } + + // Check for typo tolerance + for (let i = 0; i <= target.length - source.length; i++) { + const substring = target.substring(i, i + source.length); + let mismatchCount = 0; + for (let j = 0; j < source.length; j++) { + if (source[j] !== substring[j]) { + mismatchCount++; + } + } + if (mismatchCount <= maxTyposAllowed) { + return true; + } + } + + return false; + }; + + const preprocessPairName = (name: string): string => + name.toLowerCase().replace(/\//g, " "); + const preprocessToken = (token: { + symbol: string; + name: string; + }): { symbol: string; name: string } => ({ + symbol: token.symbol.toLowerCase(), + name: token.name.toLowerCase(), + }); + + return pairsList.filter((pair) => { + const pairName = preprocessPairName(pair.name); + const pairNameReversed = pairName.split(" ").reverse().join(" "); + const token1 = preprocessToken(pair.token1); + const token2 = preprocessToken(pair.token2); + + const nameMatches = [ + pairName, + pairNameReversed, + token1.symbol, + token2.symbol, + token1.name, + token2.name, + ]; + + return nameMatches.some( + (nameMatch) => + nameMatch.includes(searchQuery) || + hasTypoTolerance(searchQuery, nameMatch) + ); + }); + }; + const onQueryChange = (userInputQuery: string) => { - const sortedOptions = sortOptions( - options.filter( - (option) => - option["name"].toLowerCase().indexOf(userInputQuery.toLowerCase()) > - -1 - ) - ); + const filteredPairs = searchPairs(userInputQuery, options); + const sortedOptions = sortOptions(filteredPairs); + setFilteredOptions(sortedOptions); setQuery(userInputQuery); // Reset the current selected option to the first one that is available From 144b2866ffcde1718ddb08063c7f8a507997d5d9 Mon Sep 17 00:00:00 2001 From: saidam90 Date: Mon, 2 Sep 2024 02:37:04 +0400 Subject: [PATCH 3/6] Add tests and move the functio to utils.ts --- src/app/components/PairSelector.tsx | 65 +----------- src/app/utils.test.ts | 147 ++++++++++++++++++++++++++++ src/app/utils.ts | 63 ++++++++++++ 3 files changed, 211 insertions(+), 64 deletions(-) diff --git a/src/app/components/PairSelector.tsx b/src/app/components/PairSelector.tsx index f5d97d13..06d646a3 100644 --- a/src/app/components/PairSelector.tsx +++ b/src/app/components/PairSelector.tsx @@ -4,6 +4,7 @@ import { orderInputSlice } from "../state/orderInputSlice"; import { useCallback, useEffect, useMemo, useRef, useState } from "react"; import Image from "next/image"; import React from "react"; +import { searchPairs } from "utils"; import { BLACKLISTED_PAIRS } from "../data/BLACKLISTED_PAIRS"; @@ -162,70 +163,6 @@ export function PairSelector() { }; }, [handleKeyDown]); - const searchPairs = (query: string, pairsList: PairInfo[]): PairInfo[] => { - const searchQuery = query.trim().toLowerCase().replace(/\s+/g, " "); - - const hasTypoTolerance = (source: string, target: string): boolean => { - const maxTyposAllowed = Math.floor(source.length / 4); - - if (source.length > target.length + maxTyposAllowed) { - return false; - } - - if (target.includes(source)) { - return true; - } - - // Check for typo tolerance - for (let i = 0; i <= target.length - source.length; i++) { - const substring = target.substring(i, i + source.length); - let mismatchCount = 0; - for (let j = 0; j < source.length; j++) { - if (source[j] !== substring[j]) { - mismatchCount++; - } - } - if (mismatchCount <= maxTyposAllowed) { - return true; - } - } - - return false; - }; - - const preprocessPairName = (name: string): string => - name.toLowerCase().replace(/\//g, " "); - const preprocessToken = (token: { - symbol: string; - name: string; - }): { symbol: string; name: string } => ({ - symbol: token.symbol.toLowerCase(), - name: token.name.toLowerCase(), - }); - - return pairsList.filter((pair) => { - const pairName = preprocessPairName(pair.name); - const pairNameReversed = pairName.split(" ").reverse().join(" "); - const token1 = preprocessToken(pair.token1); - const token2 = preprocessToken(pair.token2); - - const nameMatches = [ - pairName, - pairNameReversed, - token1.symbol, - token2.symbol, - token1.name, - token2.name, - ]; - - return nameMatches.some( - (nameMatch) => - nameMatch.includes(searchQuery) || - hasTypoTolerance(searchQuery, nameMatch) - ); - }); - }; - const onQueryChange = (userInputQuery: string) => { const filteredPairs = searchPairs(userInputQuery, options); const sortedOptions = sortOptions(filteredPairs); diff --git a/src/app/utils.test.ts b/src/app/utils.test.ts index 3847a81c..ec652974 100644 --- a/src/app/utils.test.ts +++ b/src/app/utils.test.ts @@ -6,6 +6,8 @@ import { shortenWalletAddress, } from "./utils"; +import { searchPairs } from "./utils"; + // the separators are set to "." and " " for testing purposes // inside jest.setup.js describe("displayAmount", () => { @@ -344,3 +346,148 @@ describe("shortenWalletAddress", () => { expect(result).toBe(expectedShortened); }); }); + +describe("searchPairs", () => { + const pairsList: any = [ + { + name: "DEXTR/XRD", + token1: { + name: "Dexter", + symbol: "DEXTR", + }, + token2: { + name: "Radix", + symbol: "XRD", + }, + }, + { + name: "ADEX/XRD", + token1: { + name: "Adex", + symbol: "ADEX", + }, + token2: { + name: "Radix", + symbol: "XRD", + }, + }, + { + name: "CUPPA/XRD", + token1: { + name: "Cuppa", + symbol: "CUPPA", + }, + token2: { + name: "Radix", + symbol: "XRD", + }, + }, + ]; + + test("should find pairs by full name", () => { + const result = searchPairs("DEXTR/XRD", pairsList); + expect(result).toEqual([ + { + name: "DEXTR/XRD", + token1: { + name: "Dexter", + symbol: "DEXTR", + }, + token2: { + name: "Radix", + symbol: "XRD", + }, + }, + ]); + }); + + test("should find pairs by symbol", () => { + const result = searchPairs("DEXTR", pairsList); + expect(result).toEqual([ + { + name: "DEXTR/XRD", + token1: { + name: "Dexter", + symbol: "DEXTR", + }, + token2: { + name: "Radix", + symbol: "XRD", + }, + }, + ]); + }); + + test("should find pairs by symbol and full name with different case", () => { + const result = searchPairs("cupPa", pairsList); + expect(result).toEqual([ + { + name: "CUPPA/XRD", + token1: { + name: "Cuppa", + symbol: "CUPPA", + }, + token2: { + name: "Radix", + symbol: "XRD", + }, + }, + ]); + }); + + test("should find pairs with space as delimiter", () => { + const result = searchPairs("DEXTR XRD", pairsList); + expect(result).toEqual([ + { + name: "DEXTR/XRD", + token1: { + name: "Dexter", + symbol: "DEXTR", + }, + token2: { + name: "Radix", + symbol: "XRD", + }, + }, + ]); + }); + + test("should find pairs with swapped order of tokens", () => { + const result = searchPairs("XRD DEXTR", pairsList); + expect(result).toEqual([ + { + name: "DEXTR/XRD", + token1: { + name: "Dexter", + symbol: "DEXTR", + }, + token2: { + name: "Radix", + symbol: "XRD", + }, + }, + ]); + }); + + test("should handle minimal typos", () => { + const result = searchPairs("D3XTR", pairsList); + expect(result).toEqual([ + { + name: "DEXTR/XRD", + token1: { + name: "Dexter", + symbol: "DEXTR", + }, + token2: { + name: "Radix", + symbol: "XRD", + }, + }, + ]); + }); + + test("should return an empty array if no matches are found", () => { + const result = searchPairs("XYZ", pairsList); + expect(result).toEqual([]); + }); +}); diff --git a/src/app/utils.ts b/src/app/utils.ts index e540f6a9..4ac0084d 100644 --- a/src/app/utils.ts +++ b/src/app/utils.ts @@ -540,3 +540,66 @@ export function getLocalStoragePaginationValue(id?: string) { return undefined; } + +export const searchPairs = (query: string, pairsList: any): any => { + const searchQuery = query.trim().toLowerCase().replace(/\s+/g, " "); + + const hasTypoTolerance = (source: string, target: string): boolean => { + const maxTyposAllowed = Math.floor(source.length / 4); + + if (source.length > target.length + maxTyposAllowed) { + return false; + } + + if (target.includes(source)) { + return true; + } + + for (let i = 0; i <= target.length - source.length; i++) { + const substring = target.substring(i, i + source.length); + let mismatchCount = 0; + for (let j = 0; j < source.length; j++) { + if (source[j] !== substring[j]) { + mismatchCount++; + } + } + if (mismatchCount <= maxTyposAllowed) { + return true; + } + } + + return false; + }; + + const preprocessPairName = (name: string): string => + name.toLowerCase().replace(/\//g, " "); + const preprocessToken = (token: { + symbol: string; + name: string; + }): { symbol: string; name: string } => ({ + symbol: token.symbol.toLowerCase(), + name: token.name.toLowerCase(), + }); + + return pairsList.filter((pair: any) => { + const pairName = preprocessPairName(pair.name); + const pairNameReversed = pairName.split(" ").reverse().join(" "); + const token1 = preprocessToken(pair.token1); + const token2 = preprocessToken(pair.token2); + + const nameMatches = [ + pairName, + pairNameReversed, + token1.symbol, + token2.symbol, + token1.name, + token2.name, + ]; + + return nameMatches.some( + (nameMatch) => + nameMatch.includes(searchQuery) || + hasTypoTolerance(searchQuery, nameMatch) + ); + }); +}; From 9419ff316372e77c7e35c8d051f2c0ee2fc58bce Mon Sep 17 00:00:00 2001 From: saidam90 Date: Mon, 2 Sep 2024 12:04:09 +0400 Subject: [PATCH 4/6] Implement typo tolerance using Levenshtein distance and add more tests --- src/app/utils.test.ts | 80 ++++++++++++++++++++++++++++++++++++++++++- src/app/utils.ts | 68 +++++++++++++++++++++++------------- 2 files changed, 123 insertions(+), 25 deletions(-) diff --git a/src/app/utils.test.ts b/src/app/utils.test.ts index ec652974..9daf8884 100644 --- a/src/app/utils.test.ts +++ b/src/app/utils.test.ts @@ -385,7 +385,7 @@ describe("searchPairs", () => { ]; test("should find pairs by full name", () => { - const result = searchPairs("DEXTR/XRD", pairsList); + const result = searchPairs("Radix", pairsList); expect(result).toEqual([ { name: "DEXTR/XRD", @@ -398,6 +398,28 @@ describe("searchPairs", () => { symbol: "XRD", }, }, + { + name: "ADEX/XRD", + token1: { + name: "Adex", + symbol: "ADEX", + }, + token2: { + name: "Radix", + symbol: "XRD", + }, + }, + { + name: "CUPPA/XRD", + token1: { + name: "Cuppa", + symbol: "CUPPA", + }, + token2: { + name: "Radix", + symbol: "XRD", + }, + }, ]); }); @@ -490,4 +512,60 @@ describe("searchPairs", () => { const result = searchPairs("XYZ", pairsList); expect(result).toEqual([]); }); + + test("name with different case", () => { + const result = searchPairs("RaDIx", pairsList); + expect(result).toEqual([ + { + name: "DEXTR/XRD", + token1: { + name: "Dexter", + symbol: "DEXTR", + }, + token2: { + name: "Radix", + symbol: "XRD", + }, + }, + { + name: "ADEX/XRD", + token1: { + name: "Adex", + symbol: "ADEX", + }, + token2: { + name: "Radix", + symbol: "XRD", + }, + }, + { + name: "CUPPA/XRD", + token1: { + name: "Cuppa", + symbol: "CUPPA", + }, + token2: { + name: "Radix", + symbol: "XRD", + }, + }, + ]); + }); + + test("should find pairs by full name and lowercased", () => { + const result = searchPairs("Radix dexter", pairsList); + expect(result).toEqual([ + { + name: "DEXTR/XRD", + token1: { + name: "Dexter", + symbol: "DEXTR", + }, + token2: { + name: "Radix", + symbol: "XRD", + }, + }, + ]); + }); }); diff --git a/src/app/utils.ts b/src/app/utils.ts index 4ac0084d..89193fa9 100644 --- a/src/app/utils.ts +++ b/src/app/utils.ts @@ -541,53 +541,69 @@ export function getLocalStoragePaginationValue(id?: string) { return undefined; } -export const searchPairs = (query: string, pairsList: any): any => { +// TODO: Update input and return types to `PairInfo[]`. Currently using `any` // due to unresolved issues. Investigate the cause of the problem. +export function searchPairs(query: string, pairsList: any): any { const searchQuery = query.trim().toLowerCase().replace(/\s+/g, " "); - const hasTypoTolerance = (source: string, target: string): boolean => { - const maxTyposAllowed = Math.floor(source.length / 4); + function levenshteinDistance(a: string, b: string): number { + const matrix: number[][] = Array.from({ length: a.length + 1 }, () => []); - if (source.length > target.length + maxTyposAllowed) { - return false; + for (let i = 0; i <= a.length; i++) { + matrix[i][0] = i; } - - if (target.includes(source)) { - return true; + for (let j = 0; j <= b.length; j++) { + matrix[0][j] = j; } - for (let i = 0; i <= target.length - source.length; i++) { - const substring = target.substring(i, i + source.length); - let mismatchCount = 0; - for (let j = 0; j < source.length; j++) { - if (source[j] !== substring[j]) { - mismatchCount++; + for (let i = 1; i <= a.length; i++) { + for (let j = 1; j <= b.length; j++) { + if (a[i - 1] === b[j - 1]) { + matrix[i][j] = matrix[i - 1][j - 1]; + } else { + matrix[i][j] = Math.min( + matrix[i - 1][j] + 1, // deletion + matrix[i][j - 1] + 1, // insertion + matrix[i - 1][j - 1] + 1 // substitution + ); } } - if (mismatchCount <= maxTyposAllowed) { - return true; - } } - return false; + return matrix[a.length][b.length]; + } + + const hasTypoTolerance = (source: string, target: string): boolean => { + const maxTyposAllowed = Math.floor(source.length / 5); + const distance = levenshteinDistance(source, target); + return distance <= maxTyposAllowed; }; const preprocessPairName = (name: string): string => name.toLowerCase().replace(/\//g, " "); - const preprocessToken = (token: { - symbol: string; - name: string; - }): { symbol: string; name: string } => ({ + + const preprocessToken = (token: { symbol: string; name: string }) => ({ symbol: token.symbol.toLowerCase(), name: token.name.toLowerCase(), }); + const generateCombinations = (items: string[]): string[] => { + const combinations: string[] = []; + for (let i = 0; i < items.length; i++) { + for (let j = i + 1; j < items.length; j++) { + combinations.push(`${items[i]} ${items[j]}`); + combinations.push(`${items[j]} ${items[i]}`); + } + } + return combinations; + }; + return pairsList.filter((pair: any) => { const pairName = preprocessPairName(pair.name); const pairNameReversed = pairName.split(" ").reverse().join(" "); const token1 = preprocessToken(pair.token1); const token2 = preprocessToken(pair.token2); - const nameMatches = [ + const baseMatches = [ pairName, pairNameReversed, token1.symbol, @@ -596,10 +612,14 @@ export const searchPairs = (query: string, pairsList: any): any => { token2.name, ]; + const dynamicMatches = generateCombinations(baseMatches); + + const nameMatches = [...baseMatches, ...dynamicMatches]; + return nameMatches.some( (nameMatch) => nameMatch.includes(searchQuery) || hasTypoTolerance(searchQuery, nameMatch) ); }); -}; +} From 1f7ba6070541185d99dc41e6130faf026f238210 Mon Sep 17 00:00:00 2001 From: saidam90 Date: Mon, 2 Sep 2024 14:03:55 +0400 Subject: [PATCH 5/6] Delete commented out code --- src/app/utils.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/app/utils.ts b/src/app/utils.ts index 89193fa9..81c9008d 100644 --- a/src/app/utils.ts +++ b/src/app/utils.ts @@ -561,9 +561,9 @@ export function searchPairs(query: string, pairsList: any): any { matrix[i][j] = matrix[i - 1][j - 1]; } else { matrix[i][j] = Math.min( - matrix[i - 1][j] + 1, // deletion - matrix[i][j - 1] + 1, // insertion - matrix[i - 1][j - 1] + 1 // substitution + matrix[i - 1][j] + 1, + matrix[i][j - 1] + 1, + matrix[i - 1][j - 1] + 1 ); } } From af6d625babd6b558eca913f4363051ea1650322e Mon Sep 17 00:00:00 2001 From: saidam90 Date: Tue, 3 Sep 2024 08:33:35 +0400 Subject: [PATCH 6/6] Fix copy paste error --- src/app/utils.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/app/utils.ts b/src/app/utils.ts index 81c9008d..56d38f1f 100644 --- a/src/app/utils.ts +++ b/src/app/utils.ts @@ -541,7 +541,8 @@ export function getLocalStoragePaginationValue(id?: string) { return undefined; } -// TODO: Update input and return types to `PairInfo[]`. Currently using `any` // due to unresolved issues. Investigate the cause of the problem. +// TODO: Update input and return types to `PairInfo[]`. Currently using `any` +// due to unresolved issues. Investigate the cause of the problem. export function searchPairs(query: string, pairsList: any): any { const searchQuery = query.trim().toLowerCase().replace(/\s+/g, " ");