diff --git a/releases/unreleased/identities-management-ui.yml b/releases/unreleased/identities-management-ui.yml new file mode 100644 index 0000000..e261112 --- /dev/null +++ b/releases/unreleased/identities-management-ui.yml @@ -0,0 +1,8 @@ +--- +title: Identities management UI +category: added +author: Eva Millán +issue: null +notes: > + Manage contributor identities directly from the + user interface. diff --git a/ui/package.json b/ui/package.json index addffd6..9026b20 100644 --- a/ui/package.json +++ b/ui/package.json @@ -21,6 +21,7 @@ "axios": "^1.12.0", "js-cookie": "^3.0.5", "pinia": "^3.0.3", + "sortinghat-ui-core": "https://github.com/evamillan/grimoirelab-sortinghat/releases/download/1.13.0-rc.1/sortinghat-ui.tgz", "vue": "^3.5.18", "vue-router": "^4.5.1", "vuetify": "^3.9.2" diff --git a/ui/src/App.vue b/ui/src/App.vue index 125b859..6b80693 100644 --- a/ui/src/App.vue +++ b/ui/src/App.vue @@ -61,6 +61,12 @@ provide('createEcosystem', API.ecosystem.create) Tasks + + + Identities + diff --git a/ui/src/main.js b/ui/src/main.js index af5afe4..99d6489 100644 --- a/ui/src/main.js +++ b/ui/src/main.js @@ -5,10 +5,18 @@ import { createPinia } from 'pinia' import App from './App.vue' import router from './router' import vuetify from './plugins/vuetify' +import sortinghat from 'sortinghat-ui-core' +import 'sortinghat-ui-core/dist/sortinghat-ui.css' +import { base } from './services/api/client' const pinia = createPinia() const app = createApp(App) app.use(router).use(vuetify).use(pinia) +app.use(sortinghat, { + apiURL: `${base}/api/v1/identities/`, + router: router +}) + app.mount('#app') diff --git a/ui/src/plugins/vuetify.js b/ui/src/plugins/vuetify.js index 14b3635..bc46ad8 100644 --- a/ui/src/plugins/vuetify.js +++ b/ui/src/plugins/vuetify.js @@ -22,6 +22,8 @@ export default createVuetify({ 'on-background': '#1f2328', surface: '#ffffff', 'on-surface': '#1f2328', + 'surface-variant': '#dee3eb', + 'on-surface-variant': '#42474e', running: '#f4bc00', new: '#3fa500', enqueued: '#003756', diff --git a/ui/src/router/index.js b/ui/src/router/index.js index 34c88d6..37681cb 100644 --- a/ui/src/router/index.js +++ b/ui/src/router/index.js @@ -1,5 +1,6 @@ import { createRouter, createWebHistory } from 'vue-router' import { useUserStore } from '@/store' +import sortinghat from 'sortinghat-ui-core' const router = createRouter({ history: createWebHistory(import.meta.env.BASE_URL), @@ -85,6 +86,18 @@ const router = createRouter({ name: 'signIn', component: () => import('../views/SignIn.vue') }, + { + path: '/identities', + name: 'identities', + children: sortinghat.routes, + redirect: { name: 'Dashboard' }, + meta: { + breadcrumb: { + title: 'Identities', + to: { name: 'Dashboard' } + } + } + }, { path: '/:pathMatch(.*)*', name: 'notFound', component: () => import('../views/NotFound.vue') } ] }) diff --git a/ui/src/services/api/client.js b/ui/src/services/api/client.js index 7925f3e..c0be638 100644 --- a/ui/src/services/api/client.js +++ b/ui/src/services/api/client.js @@ -5,7 +5,7 @@ import { useUserStore } from '@/store' const AUTHENTICATION_ERROR = 'Authentication credentials were not provided.' const defaultBase = import.meta.env.MODE === 'development' ? 'http://localhost:8000' : '/' -const base = import.meta.env.VITE_API_ENDPOINT || defaultBase +export const base = import.meta.env.VITE_API_ENDPOINT || defaultBase export const client = axios.create({ baseURL: base, diff --git a/ui/yarn.lock b/ui/yarn.lock index 67ea23a..8b1d398 100644 --- a/ui/yarn.lock +++ b/ui/yarn.lock @@ -43,6 +43,11 @@ resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.27.1.tgz#a7054dcc145a967dd4dc8fee845a57c1316c9df8" integrity sha512-D2hP9eA+Sqx1kBZgzxZh0y1trbuU+JoDkiEwqhQ36nodYqJwyEIhPSdMNd7lOm/4io72luTPWH20Yda0xOuUow== +"@babel/helper-validator-identifier@^7.28.5": + version "7.28.5" + resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.28.5.tgz#010b6938fab7cb7df74aa2bbc06aa503b8fe5fb4" + integrity sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q== + "@babel/parser@^7.21.4", "@babel/parser@^7.24.4", "@babel/parser@^7.6.0", "@babel/parser@^7.9.6": version "7.24.4" resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.24.4.tgz#234487a110d89ad5a3ed4a8a566c36b9453e8c88" @@ -55,6 +60,13 @@ dependencies: "@babel/types" "^7.28.0" +"@babel/parser@^7.28.5": + version "7.28.5" + resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.28.5.tgz#0b0225ee90362f030efd644e8034c99468893b08" + integrity sha512-KKBU1VGYR7ORr3At5HAtUQ+TV3SzRCXmA/8OdDZiLDBIZxVyzXuztPjfLd3BV1PRAQGCMWWSHYhL0F8d5uHBDQ== + dependencies: + "@babel/types" "^7.28.5" + "@babel/types@^7.21.4", "@babel/types@^7.6.1", "@babel/types@^7.9.6": version "7.24.0" resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.24.0.tgz#3b951f435a92e7333eba05b7566fd297960ea1bf" @@ -72,6 +84,14 @@ "@babel/helper-string-parser" "^7.27.1" "@babel/helper-validator-identifier" "^7.27.1" +"@babel/types@^7.28.5": + version "7.28.5" + resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.28.5.tgz#10fc405f60897c35f07e85493c932c7b5ca0592b" + integrity sha512-qQ5m48eI/MFLQ5PxQj4PFaprjyCTLI37ElWMmNs0K8Lk3dVeOdNpB3ks8jc7yM5CDmVC73eMVk/trk3fgmrUpA== + dependencies: + "@babel/helper-string-parser" "^7.27.1" + "@babel/helper-validator-identifier" "^7.28.5" + "@csstools/color-helpers@^5.0.2": version "5.0.2" resolved "https://registry.yarnpkg.com/@csstools/color-helpers/-/color-helpers-5.0.2.tgz#82592c9a7c2b83c293d9161894e2a6471feb97b8" @@ -349,6 +369,11 @@ resolved "https://registry.yarnpkg.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.4.tgz#7358043433b2e5da569aa02cbc4c121da3af27d7" integrity sha512-VT2+G1VQs/9oz078bLrYbecdZKs912zQlkelYpuf+SXF+QvZDYJlbx/LSx+meSAwdDFnF8FVXW92AVjjkVmgFw== +"@jridgewell/sourcemap-codec@^1.5.5": + version "1.5.5" + resolved "https://registry.yarnpkg.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz#6912b00d2c631c0d15ce1a7ab57cd657f2a8f8ba" + integrity sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og== + "@mdi/font@^7.4.47": version "7.4.47" resolved "https://registry.yarnpkg.com/@mdi/font/-/font-7.4.47.tgz#2ae522867da3a5c88b738d54b403eb91471903af" @@ -956,6 +981,17 @@ estree-walker "^2.0.2" source-map-js "^1.2.1" +"@vue/compiler-core@3.5.24": + version "3.5.24" + resolved "https://registry.yarnpkg.com/@vue/compiler-core/-/compiler-core-3.5.24.tgz#1853f4b7d7090033cd9041aab6e7e8017d66c39c" + integrity sha512-eDl5H57AOpNakGNAkFDH+y7kTqrQpJkZFXhWZQGyx/5Wh7B1uQYvcWkvZi11BDhscPgj8N7XV3oRwiPnx1Vrig== + dependencies: + "@babel/parser" "^7.28.5" + "@vue/shared" "3.5.24" + entities "^4.5.0" + estree-walker "^2.0.2" + source-map-js "^1.2.1" + "@vue/compiler-dom@3.4.24", "@vue/compiler-dom@^3.2.0": version "3.4.24" resolved "https://registry.yarnpkg.com/@vue/compiler-dom/-/compiler-dom-3.4.24.tgz#b7335a49f095b6d35e48b6f7be8da513c1fa52b8" @@ -972,6 +1008,14 @@ "@vue/compiler-core" "3.5.18" "@vue/shared" "3.5.18" +"@vue/compiler-dom@3.5.24": + version "3.5.24" + resolved "https://registry.yarnpkg.com/@vue/compiler-dom/-/compiler-dom-3.5.24.tgz#b02e98749c377d6d2ba30dc2e94ce0f5b0af060c" + integrity sha512-1QHGAvs53gXkWdd3ZMGYuvQFXHW4ksKWPG8HP8/2BscrbZ0brw183q2oNWjMrSWImYLHxHrx1ItBQr50I/q2zw== + dependencies: + "@vue/compiler-core" "3.5.24" + "@vue/shared" "3.5.24" + "@vue/compiler-sfc@3.5.18": version "3.5.18" resolved "https://registry.yarnpkg.com/@vue/compiler-sfc/-/compiler-sfc-3.5.18.tgz#ba1e849561337d809937994cdaf900539542eeca" @@ -987,6 +1031,21 @@ postcss "^8.5.6" source-map-js "^1.2.1" +"@vue/compiler-sfc@3.5.24": + version "3.5.24" + resolved "https://registry.yarnpkg.com/@vue/compiler-sfc/-/compiler-sfc-3.5.24.tgz#7cc3329b672b7b75d04a71eabe81bc84209dfa9f" + integrity sha512-8EG5YPRgmTB+YxYBM3VXy8zHD9SWHUJLIGPhDovo3Z8VOgvP+O7UP5vl0J4BBPWYD9vxtBabzW1EuEZ+Cqs14g== + dependencies: + "@babel/parser" "^7.28.5" + "@vue/compiler-core" "3.5.24" + "@vue/compiler-dom" "3.5.24" + "@vue/compiler-ssr" "3.5.24" + "@vue/shared" "3.5.24" + estree-walker "^2.0.2" + magic-string "^0.30.21" + postcss "^8.5.6" + source-map-js "^1.2.1" + "@vue/compiler-sfc@^3.2.0": version "3.4.24" resolved "https://registry.yarnpkg.com/@vue/compiler-sfc/-/compiler-sfc-3.4.24.tgz#2872e353147ce2a145169a33ddd4d68dc95c3a18" @@ -1018,6 +1077,14 @@ "@vue/compiler-dom" "3.5.18" "@vue/shared" "3.5.18" +"@vue/compiler-ssr@3.5.24": + version "3.5.24" + resolved "https://registry.yarnpkg.com/@vue/compiler-ssr/-/compiler-ssr-3.5.24.tgz#3891f2479928751353b946e491e048f33d0249a8" + integrity sha512-trOvMWNBMQ/odMRHW7Ae1CdfYx+7MuiQu62Jtu36gMLXcaoqKvAyh+P73sYG9ll+6jLB6QPovqoKGGZROzkFFg== + dependencies: + "@vue/compiler-dom" "3.5.24" + "@vue/shared" "3.5.24" + "@vue/compiler-vue2@^2.7.16": version "2.7.16" resolved "https://registry.yarnpkg.com/@vue/compiler-vue2/-/compiler-vue2-2.7.16.tgz#2ba837cbd3f1b33c2bc865fbe1a3b53fb611e249" @@ -1087,6 +1154,13 @@ dependencies: "@vue/shared" "3.5.18" +"@vue/reactivity@3.5.24": + version "3.5.24" + resolved "https://registry.yarnpkg.com/@vue/reactivity/-/reactivity-3.5.24.tgz#d2de10045f3ec05108787b6c90701b3f3bc401f8" + integrity sha512-BM8kBhtlkkbnyl4q+HiF5R5BL0ycDPfihowulm02q3WYp2vxgPcJuZO866qa/0u3idbMntKEtVNuAUp5bw4teg== + dependencies: + "@vue/shared" "3.5.24" + "@vue/runtime-core@3.5.18": version "3.5.18" resolved "https://registry.yarnpkg.com/@vue/runtime-core/-/runtime-core-3.5.18.tgz#9e9ae8b9491548b53d0cea2bf25746d27c52e191" @@ -1095,6 +1169,14 @@ "@vue/reactivity" "3.5.18" "@vue/shared" "3.5.18" +"@vue/runtime-core@3.5.24": + version "3.5.24" + resolved "https://registry.yarnpkg.com/@vue/runtime-core/-/runtime-core-3.5.24.tgz#902ff6f6372a14b190aeb501a57467759bc58241" + integrity sha512-RYP/byyKDgNIqfX/gNb2PB55dJmM97jc9wyF3jK7QUInYKypK2exmZMNwnjueWwGceEkP6NChd3D2ZVEp9undQ== + dependencies: + "@vue/reactivity" "3.5.24" + "@vue/shared" "3.5.24" + "@vue/runtime-dom@3.5.18": version "3.5.18" resolved "https://registry.yarnpkg.com/@vue/runtime-dom/-/runtime-dom-3.5.18.tgz#1150952d1048b5822e4f1dd8aed24665cbb22107" @@ -1105,6 +1187,16 @@ "@vue/shared" "3.5.18" csstype "^3.1.3" +"@vue/runtime-dom@3.5.24": + version "3.5.24" + resolved "https://registry.yarnpkg.com/@vue/runtime-dom/-/runtime-dom-3.5.24.tgz#b27414fd0be2c9b58c701a67ba4db96b78819e9b" + integrity sha512-Z8ANhr/i0XIluonHVjbUkjvn+CyrxbXRIxR7wn7+X7xlcb7dJsfITZbkVOeJZdP8VZwfrWRsWdShH6pngMxRjw== + dependencies: + "@vue/reactivity" "3.5.24" + "@vue/runtime-core" "3.5.24" + "@vue/shared" "3.5.24" + csstype "^3.1.3" + "@vue/server-renderer@3.5.18": version "3.5.18" resolved "https://registry.yarnpkg.com/@vue/server-renderer/-/server-renderer-3.5.18.tgz#e9fa267b95b3a1d8cddca762377e5de2ae9122bd" @@ -1113,6 +1205,14 @@ "@vue/compiler-ssr" "3.5.18" "@vue/shared" "3.5.18" +"@vue/server-renderer@3.5.24": + version "3.5.24" + resolved "https://registry.yarnpkg.com/@vue/server-renderer/-/server-renderer-3.5.24.tgz#efc5f8dc2776b91f04bf3249be95afe6e83a02a8" + integrity sha512-Yh2j2Y4G/0/4z/xJ1Bad4mxaAk++C2v4kaa8oSYTMJBJ00/ndPuxCnWeot0/7/qafQFLh5pr6xeV6SdMcE/G1w== + dependencies: + "@vue/compiler-ssr" "3.5.24" + "@vue/shared" "3.5.24" + "@vue/shared@3.4.24": version "3.4.24" resolved "https://registry.yarnpkg.com/@vue/shared/-/shared-3.4.24.tgz#278ac71f492b392b9b17fe8fc7d324db1a8842db" @@ -1123,6 +1223,11 @@ resolved "https://registry.yarnpkg.com/@vue/shared/-/shared-3.5.18.tgz#529f24a88d3ed678d50fd5c07455841fbe8ac95e" integrity sha512-cZy8Dq+uuIXbxCZpuLd2GJdeSO/lIzIspC2WtkqIpje5QyFbvLaI5wZtdUjLHjGZrlVX6GilejatWwVYYRc8tA== +"@vue/shared@3.5.24": + version "3.5.24" + resolved "https://registry.yarnpkg.com/@vue/shared/-/shared-3.5.24.tgz#45ea9e6e037e53cfb8141ffa6bcad75b8be11e9c" + integrity sha512-9cwHL2EsJBdi8NY22pngYYWzkTDhld6fAD6jlaeloNGciNSJL6bLpbxVgXl96X00Jtc6YWQv96YA/0sxex/k1A== + "@vue/test-utils@^2.4.6": version "2.4.6" resolved "https://registry.yarnpkg.com/@vue/test-utils/-/test-utils-2.4.6.tgz#7d534e70c4319d2a587d6a3b45a39e9695ade03c" @@ -3063,6 +3168,13 @@ magic-string@^0.30.17: dependencies: "@jridgewell/sourcemap-codec" "^1.5.0" +magic-string@^0.30.21: + version "0.30.21" + resolved "https://registry.yarnpkg.com/magic-string/-/magic-string-0.30.21.tgz#56763ec09a0fa8091df27879fd94d19078c00d91" + integrity sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ== + dependencies: + "@jridgewell/sourcemap-codec" "^1.5.5" + map-stream@~0.1.0: version "0.1.0" resolved "https://registry.yarnpkg.com/map-stream/-/map-stream-0.1.0.tgz#e56aa94c4c8055a16404a0674b78f215f7c8e194" @@ -3867,6 +3979,13 @@ slice-ansi@^4.0.0: astral-regex "^2.0.0" is-fullwidth-code-point "^3.0.0" +"sortinghat-ui-core@https://github.com/evamillan/grimoirelab-sortinghat/releases/download/1.13.0-rc.1/sortinghat-ui.tgz": + version "0.0.0" + resolved "https://github.com/evamillan/grimoirelab-sortinghat/releases/download/1.13.0-rc.1/sortinghat-ui.tgz#2f658e9b8352fcb70a8c025b0b2e11060911c7f1" + integrity sha512-RwTQY2F0+dTvMZNK0PPI0d72Yxncc4RxMEnvXpxVafuvn1+72taIg3ciwG4FtS7ZnQ4C2IVp2789uWAFfx1LcQ== sha1-L2WOm4NS/LcKjAJbCy4RBgkRx/E= sha256-BisHU+/vYyqfOGGbujIK56YsQQmcT2VHuAfYgFsV34A= + dependencies: + vue "^3.5.22" + "source-map-js@>=0.6.2 <2.0.0", source-map-js@^1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/source-map-js/-/source-map-js-1.2.0.tgz#16b809c162517b5b8c3e7dcd315a2a5c2612b2af" @@ -3957,7 +4076,7 @@ stream-combiner@~0.0.4: dependencies: duplexer "~0.1.1" -"string-width-cjs@npm:string-width@^4.2.0": +"string-width-cjs@npm:string-width@^4.2.0", string-width@^4.1.0, string-width@^4.2.0, string-width@^5.0.1, string-width@^5.1.2: version "4.2.3" resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== @@ -3966,23 +4085,7 @@ stream-combiner@~0.0.4: is-fullwidth-code-point "^3.0.0" strip-ansi "^6.0.1" -string-width@^4.1.0, string-width@^4.2.0, string-width@^5.0.1, string-width@^5.1.2: - version "4.2.3" - resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" - integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== - dependencies: - emoji-regex "^8.0.0" - is-fullwidth-code-point "^3.0.0" - strip-ansi "^6.0.1" - -"strip-ansi-cjs@npm:strip-ansi@^6.0.1": - version "6.0.1" - resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" - integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== - dependencies: - ansi-regex "^5.0.1" - -strip-ansi@^6.0.0, strip-ansi@^6.0.1: +"strip-ansi-cjs@npm:strip-ansi@^6.0.1", strip-ansi@^6.0.0, strip-ansi@^6.0.1: version "6.0.1" resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== @@ -4426,6 +4529,17 @@ vue@^3.5.18: "@vue/server-renderer" "3.5.18" "@vue/shared" "3.5.18" +vue@^3.5.22: + version "3.5.24" + resolved "https://registry.yarnpkg.com/vue/-/vue-3.5.24.tgz#1b93af349faa064f46e70ac5b0caaa3d0952bbf3" + integrity sha512-uTHDOpVQTMjcGgrqFPSb8iO2m1DUvo+WbGqoXQz8Y1CeBYQ0FXf2z1gLRaBtHjlRz7zZUBHxjVB5VTLzYkvftg== + dependencies: + "@vue/compiler-dom" "3.5.24" + "@vue/compiler-sfc" "3.5.24" + "@vue/runtime-dom" "3.5.24" + "@vue/server-renderer" "3.5.24" + "@vue/shared" "3.5.24" + vuetify@^3.9.2: version "3.9.2" resolved "https://registry.yarnpkg.com/vuetify/-/vuetify-3.9.2.tgz#7458886c6e5922c9499a6d75001c3af2240cc374" @@ -4517,7 +4631,8 @@ with@^7.0.0: assert-never "^1.2.1" babel-walk "3.0.0-canary-5" -"wrap-ansi-cjs@npm:wrap-ansi@^7.0.0": +"wrap-ansi-cjs@npm:wrap-ansi@^7.0.0", wrap-ansi@^7.0.0: + name wrap-ansi-cjs version "7.0.0" resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43" integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q== @@ -4535,15 +4650,6 @@ wrap-ansi@^6.2.0: string-width "^4.1.0" strip-ansi "^6.0.0" -wrap-ansi@^7.0.0: - version "7.0.0" - resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43" - integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q== - dependencies: - ansi-styles "^4.0.0" - string-width "^4.1.0" - strip-ansi "^6.0.0" - wrap-ansi@^8.1.0: version "8.1.0" resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-8.1.0.tgz#56dc22368ee570face1b49819975d9b9a5ead214"