From c13613ec072a48b844a9a7ca26360e38ade7ed88 Mon Sep 17 00:00:00 2001 From: landaiqing <3517283258@qq.com> Date: Thu, 8 Aug 2024 23:45:51 +0800 Subject: [PATCH] :art: use alova / add nprogress --- .env.development | 2 +- package.json | 4 + src/api/user/index.ts | 11 + src/layout/default/Layout.vue | 5 +- src/main.ts | 16 +- src/router/modules/login.ts | 17 +- src/router/modules/test.ts | 1 + src/router/router.ts | 35 +- src/store/modules/themeStore.ts | 2 +- .../adapter/localforageStorageAdapter.ts | 17 + src/utils/alova/service.ts | 153 +++++++++ src/utils/axios/request.ts | 305 +++++++++--------- src/utils/cookie/cookie.ts | 133 ++++---- src/utils/localforage/index.ts | 2 +- src/utils/nprogress/nprogress.ts | 22 ++ src/views/Login/LoginPage.vue | 23 +- vite.config.ts | 2 +- yarn.lock | 35 ++ 18 files changed, 533 insertions(+), 252 deletions(-) create mode 100644 src/api/user/index.ts create mode 100644 src/utils/alova/adapter/localforageStorageAdapter.ts create mode 100644 src/utils/alova/service.ts create mode 100644 src/utils/nprogress/nprogress.ts diff --git a/.env.development b/.env.development index ce90d88..18b9f03 100644 --- a/.env.development +++ b/.env.development @@ -8,7 +8,7 @@ VITE_APP_BASE_API='/api' VITE_APP_TITLE=开发环境 # 网络请求公用地址 -VITE_API_BASE_URL='http://127.0.0.1:5050' +VITE_API_BASE_URL='http://127.0.0.1:8080' VITE_TITLE_NAME='五味子云相册' diff --git a/package.json b/package.json index d186dd6..97eda08 100644 --- a/package.json +++ b/package.json @@ -9,15 +9,19 @@ "preview": "vite preview" }, "dependencies": { + "@alova/adapter-axios": "^2.0.5", "@ant-design/icons-vue": "^7.0.1", "@types/crypto-js": "^4.2.2", "@types/node": "^22.1.0", + "@types/nprogress": "^0.2.3", + "alova": "^3.0.5", "ant-design-vue": "^4.2.3", "axios": "^1.7.3", "crypto-js": "^4.2.0", "eslint": "9.x", "less": "^4.2.0", "localforage": "^1.10.0", + "nprogress": "^0.2.0", "pinia": "^2.2.1", "pinia-plugin-persistedstate": "^3.2.1", "unplugin-auto-import": "^0.18.2", diff --git a/src/api/user/index.ts b/src/api/user/index.ts new file mode 100644 index 0000000..c0d41c9 --- /dev/null +++ b/src/api/user/index.ts @@ -0,0 +1,11 @@ +import {service} from "@/utils/alova/service.ts"; + + +export const getUserInfo = () => { + return service.Get('/api/auth/user/List', { + meta: { + ignoreToken: true + }, + }); + +}; diff --git a/src/layout/default/Layout.vue b/src/layout/default/Layout.vue index d505570..dcd0b3e 100644 --- a/src/layout/default/Layout.vue +++ b/src/layout/default/Layout.vue @@ -6,7 +6,10 @@ }, }" > - + + + + diff --git a/src/main.ts b/src/main.ts index f367e80..f6f4cbf 100644 --- a/src/main.ts +++ b/src/main.ts @@ -1,15 +1,15 @@ -import {createApp} from 'vue' -import App from './App.vue' -import '@/assets/styles/scroll-bar.scss' -import piniaPluginPersistedstate from 'pinia-plugin-persistedstate' +import {createApp} from 'vue'; +import App from './App.vue'; +import '@/assets/styles/scroll-bar.scss'; +import piniaPluginPersistedstate from 'pinia-plugin-persistedstate'; //Pinia -import {createPinia, Pinia} from 'pinia' +import {createPinia, Pinia} from 'pinia'; import router from "@/router/router.ts"; -const pinia: Pinia = createPinia() -pinia.use(piniaPluginPersistedstate) +const pinia: Pinia = createPinia(); +pinia.use(piniaPluginPersistedstate); createApp(App) .use(router) .use(pinia) - .mount('#app') + .mount('#app'); diff --git a/src/router/modules/login.ts b/src/router/modules/login.ts index 0914b75..8c6abe2 100644 --- a/src/router/modules/login.ts +++ b/src/router/modules/login.ts @@ -1,10 +1,11 @@ export default [ - { - path: '/', - name: 'login', - component: () => import('@/views/Login/LoginPage.vue'), - meta: { - title: '登录页' - } - } + { + path: '/', + name: 'login', + component: () => import('@/views/Login/LoginPage.vue'), + meta: { + requiresAuth: false, + title: '登录页' + } + } ]; diff --git a/src/router/modules/test.ts b/src/router/modules/test.ts index 9699fa3..d75fd65 100644 --- a/src/router/modules/test.ts +++ b/src/router/modules/test.ts @@ -4,6 +4,7 @@ export default [ name: 'test', component: () => import('@/views/TestTheme.vue'), meta: { + requiresAuth: true, title: '测试' } } diff --git a/src/router/router.ts b/src/router/router.ts index f162710..3bb1834 100644 --- a/src/router/router.ts +++ b/src/router/router.ts @@ -1,7 +1,11 @@ -import {createRouter, createWebHistory, RouteRecordRaw} from 'vue-router'; - +/* eslint-disable */ +// @ts-nocheck +import {createRouter, createWebHistory, Router, RouteRecordRaw} from 'vue-router'; import login from './modules/login'; import test from "@/router/modules/test.ts"; +import useStore from "@/store"; +import {message} from "ant-design-vue"; +import {close, start} from '@/utils/nprogress/nprogress.ts'; const routes: Array = [ @@ -10,8 +14,33 @@ const routes: Array = [ ]; -const router = createRouter({ +const router: Router = createRouter({ history: createWebHistory(), routes }); + +router.beforeEach((to, from, next) => { + start(); + if (to.meta.requiresAuth) { + const user = useStore().user; + const token: string | undefined = user.getUser()?.token; + const userId: string | undefined = user.getUser()?.userId; + if (token !== undefined && userId !== undefined) { + next(); + } else { + message.warn('请先登录').then(); + next({ + path: '/', + query: {redirect: to.fullPath} + }); + } + } else { + next(); + } +}); + +router.afterEach(() => { + // 关闭进度条 + close(); +}); export default router; diff --git a/src/store/modules/themeStore.ts b/src/store/modules/themeStore.ts index 6715e71..6df49f3 100644 --- a/src/store/modules/themeStore.ts +++ b/src/store/modules/themeStore.ts @@ -43,7 +43,7 @@ export const useThemeStore = defineStore( { persist: { key: 'theme', - paths: ['themeName','darkMode',"darkModeComp","themeConfig"], + paths: ['themeName','darkMode'], storage: handleLocalforage, serializer: { deserialize: parse, diff --git a/src/utils/alova/adapter/localforageStorageAdapter.ts b/src/utils/alova/adapter/localforageStorageAdapter.ts new file mode 100644 index 0000000..fc4ade0 --- /dev/null +++ b/src/utils/alova/adapter/localforageStorageAdapter.ts @@ -0,0 +1,17 @@ +import {handleLocalforage} from "@/utils/localforage"; + +export const localforageStorageAdapter = { + set(key: string, value: any) { + handleLocalforage.setItem(key, value); + }, + get(key: string) { + const data: string = handleLocalforage.getItem(key); + return data ? JSON.parse(data) : data; + }, + remove(key: any) { + handleLocalforage.removeItem(key); + }, + clear() { + handleLocalforage.clear().then(); + } +}; diff --git a/src/utils/alova/service.ts b/src/utils/alova/service.ts new file mode 100644 index 0000000..54e7ed0 --- /dev/null +++ b/src/utils/alova/service.ts @@ -0,0 +1,153 @@ +import {createAlova} from 'alova'; + +import VueHook from 'alova/vue'; +import useStore from "@/store"; +import {axiosRequestAdapter} from "@alova/adapter-axios"; +import {AxiosError, AxiosResponse} from "axios"; +import {message} from "ant-design-vue"; +import {localforageStorageAdapter} from "@/utils/alova/adapter/localforageStorageAdapter.ts"; + +export const service = createAlova({ + timeout: 5000, + baseURL: import.meta.env.VITE_APP_BASE_API, + statesHook: VueHook, + // 请求适配器 + requestAdapter: axiosRequestAdapter(), + l2Cache: localforageStorageAdapter, + cacheFor: { + GET: { + mode: 'restore', + expire: 60 * 10 * 1000 + }, + }, + cacheLogger: import.meta.env.VITE_NODE_ENV === 'development', + // 设置全局的请求拦截器 + beforeRequest(method) { + if (!method.meta?.ignoreToken) { + const user = useStore().user; + method.config.headers.token = user.getUser()?.token; + } + + }, + // 响应拦截器 + responded: { + onSuccess: async (response: AxiosResponse) => { + if (response.data instanceof Blob) { + return response; + } else { + return response.data; + } + }, + onError(error: AxiosError) { + const {response} = error; + if (response) { + handleCode(response.status); + } + if (!window.navigator.onLine) { + message.error("网络连接失败").then(); + return Promise.reject(error); + } + } + } +}); + +function handleCode(code: number): void { + switch (code) { + case 400: + message + .open({ + content: "请求错误(400)", + type: "error", + }) + .then(); + break; + case 401: + message + .open({ + content: "未授权,请重新登录(401)", + type: "error", + }) + .then(); + break; + case 403: + message + .open({ + content: "拒绝访问(403)", + type: "error", + }) + .then(); + break; + case 404: + message + .open({ + content: "请求出错(404)", + type: "error", + }) + .then(); + break; + case 408: + message + .open({ + content: "请求超时(408)", + type: "error", + }) + .then(); + break; + case 500: + message + .open({ + content: "服务器错误(500)", + type: "error", + }) + .then(); + break; + case 501: + message + .open({ + content: "服务未实现(501)", + type: "error", + }) + .then(); + break; + case 502: + message + .open({ + content: "网络错误(502)", + type: "error", + }) + .then(); + break; + case 503: + message + .open({ + content: "服务不可用(503)", + type: "error", + }) + .then(); + break; + case 504: + message + .open({ + content: "网络超时(504)", + type: "error", + }) + .then(); + break; + case 505: + message + .open({ + content: "HTTP版本不受支持(505)", + type: "error", + }) + .then(); + break; + default: + message + .open({ + content: `连接出错(${code})!`, + type: "error", + }) + .then(); + break; + } +} diff --git a/src/utils/axios/request.ts b/src/utils/axios/request.ts index 55c9553..2be48a1 100644 --- a/src/utils/axios/request.ts +++ b/src/utils/axios/request.ts @@ -1,163 +1,164 @@ /** @format */ -import axios, { AxiosInstance, AxiosRequestConfig } from "axios"; -import { message } from "ant-design-vue"; -import { getStorageFromKey } from "@/utils/localStorage/config.ts"; +import axios, {AxiosInstance, AxiosRequestConfig} from "axios"; +import {message} from "ant-design-vue"; +import useStore from "@/store"; class Request { - private instance: AxiosInstance | undefined; + private instance: AxiosInstance | undefined; - constructor(config: AxiosRequestConfig) { - this.instance = axios.create(config); - // 全局请求拦截 - this.instance.interceptors.request.use( - (config) => { - const token: string | null = getStorageFromKey("token"); - if (token) { - config.headers.Authorization = `${import.meta.env.VITE_APP_TOKEN_KEY} ${token}`; - } - return config; - }, - (error) => { - return Promise.reject(error); - }, - ); + constructor(config: AxiosRequestConfig) { + this.instance = axios.create(config); + // 全局请求拦截 + this.instance.interceptors.request.use( + (config) => { + const user = useStore().user; + const token: string | undefined = user.getUser()?.token; + if (token) { + config.headers.Authorization = `${import.meta.env.VITE_APP_TOKEN_KEY} ${token}`; + } + return config; + }, + (error) => { + return Promise.reject(error); + }, + ); - // 全局响应拦截 - this.instance.interceptors.response.use( - (response) => { - if (response.data instanceof Blob) { - return response; - } else { - return response.data; - } - }, - (error) => { - const { response } = error; - if (response) { - this.handleCode(response.status); - } - if (!window.navigator.onLine) { - message.error("网络连接失败"); - return Promise.reject(error); - } - }, - ); - } + // 全局响应拦截 + this.instance.interceptors.response.use( + (response) => { + if (response.data instanceof Blob) { + return response; + } else { + return response.data; + } + }, + (error) => { + const {response} = error; + if (response) { + this.handleCode(response.status); + } + if (!window.navigator.onLine) { + message.error("网络连接失败"); + return Promise.reject(error); + } + }, + ); + } - handleCode(code: number): void { - switch (code) { - case 400: - message - .open({ - content: "请求错误(400)", - type: "error", - }) - .then(); - break; - case 401: - message - .open({ - content: "未授权,请重新登录(401)", - type: "error", - }) - .then(); - break; - case 403: - message - .open({ - content: "拒绝访问(403)", - type: "error", - }) - .then(); - break; - case 404: - message - .open({ - content: "请求出错(404)", - type: "error", - }) - .then(); - break; - case 408: - message - .open({ - content: "请求超时(408)", - type: "error", - }) - .then(); - break; - case 500: - message - .open({ - content: "服务器错误(500)", - type: "error", - }) - .then(); - break; - case 501: - message - .open({ - content: "服务未实现(501)", - type: "error", - }) - .then(); - break; - case 502: - message - .open({ - content: "网络错误(502)", - type: "error", - }) - .then(); - break; - case 503: - message - .open({ - content: "服务不可用(503)", - type: "error", - }) - .then(); - break; - case 504: - message - .open({ - content: "网络超时(504)", - type: "error", - }) - .then(); - break; - case 505: - message - .open({ - content: "HTTP版本不受支持(505)", - type: "error", - }) - .then(); - break; - default: - message - .open({ - content: `连接出错(${code})!`, - type: "error", - }) - .then(); - break; - } - } + handleCode(code: number): void { + switch (code) { + case 400: + message + .open({ + content: "请求错误(400)", + type: "error", + }) + .then(); + break; + case 401: + message + .open({ + content: "未授权,请重新登录(401)", + type: "error", + }) + .then(); + break; + case 403: + message + .open({ + content: "拒绝访问(403)", + type: "error", + }) + .then(); + break; + case 404: + message + .open({ + content: "请求出错(404)", + type: "error", + }) + .then(); + break; + case 408: + message + .open({ + content: "请求超时(408)", + type: "error", + }) + .then(); + break; + case 500: + message + .open({ + content: "服务器错误(500)", + type: "error", + }) + .then(); + break; + case 501: + message + .open({ + content: "服务未实现(501)", + type: "error", + }) + .then(); + break; + case 502: + message + .open({ + content: "网络错误(502)", + type: "error", + }) + .then(); + break; + case 503: + message + .open({ + content: "服务不可用(503)", + type: "error", + }) + .then(); + break; + case 504: + message + .open({ + content: "网络超时(504)", + type: "error", + }) + .then(); + break; + case 505: + message + .open({ + content: "HTTP版本不受支持(505)", + type: "error", + }) + .then(); + break; + default: + message + .open({ + content: `连接出错(${code})!`, + type: "error", + }) + .then(); + break; + } + } - request(config: AxiosRequestConfig): Promise { - return new Promise((resolve, reject) => { - this.instance - ?.request(config) - .then((res) => { - resolve(res); - }) - .catch((err) => { - reject(err); - }); - }); - } + request(config: AxiosRequestConfig): Promise { + return new Promise((resolve, reject) => { + this.instance + ?.request(config) + .then((res) => { + resolve(res); + }) + .catch((err) => { + reject(err); + }); + }); + } } export default Request; diff --git a/src/utils/cookie/cookie.ts b/src/utils/cookie/cookie.ts index 5b96979..e965b1d 100644 --- a/src/utils/cookie/cookie.ts +++ b/src/utils/cookie/cookie.ts @@ -5,87 +5,86 @@ */ interface Options { - /** key前缀 */ - prefix?: string; + /** key前缀 */ + prefix?: string; } /** 默认配置 */ const defaultOptions: Options = { - prefix: "", + prefix: "", }; class CookieStorage { - private prefix: string; + private prefix: string; - constructor(options: Options = defaultOptions) { - const { prefix } = options; - this.prefix = prefix ?? ""; - } + constructor(options: Options = defaultOptions) { + const {prefix} = options; + this.prefix = prefix ?? ""; + } - /** - * @description: 设置cookie - * @param {string} key 键 - * @param {any} value 值 - * @param {number} expires 过期时间s毫秒,不传默认2年有效 - * @Date: 2023-05-17 18:10:34 - * @Author: mulingyuer - */ - public setItem(key: string, value: string | number, expires?: number): void { - const timestamp = Date.now(); - if (typeof expires === "number") { - expires = timestamp + expires; - } else { - expires = timestamp + 2 * 365 * 24 * 60 * 60 * 1000; - } - document.cookie = `${this.prefix}${key}=${value};expires=${new Date(expires).toUTCString()}`; - } + /** + * @description: 设置cookie + * @param {string} key 键 + * @param {any} value 值 + * @param {number} expires 过期时间s毫秒,不传默认2年有效 + * @Date: 2023-05-17 18:10:34 + * @Author: mulingyuer + */ + public setItem(key: string, value: string | number, expires?: number): void { + const timestamp = Date.now(); + if (typeof expires === "number") { + expires = timestamp + expires; + } else { + expires = timestamp + 2 * 365 * 24 * 60 * 60 * 1000; + } + document.cookie = `${this.prefix}${key}=${value};expires=${new Date(expires).toUTCString()}`; + } - /** - * @description: 获取cookie - * @param {string} key 键 - * @Date: 2023-05-17 18:31:50 - * @Author: mulingyuer - */ - public getItem(key: string): string | undefined { - const cookies = document.cookie.split("; "); - let val: string | undefined = undefined; - cookies.find((item) => { - const [k, v] = item.split("="); - if (k === `${this.prefix}${key}`) { - val = v; - return true; - } - return false; - }); + /** + * @description: 获取cookie + * @param {string} key 键 + * @Date: 2023-05-17 18:31:50 + * @Author: mulingyuer + */ + public getItem(key: string): string | undefined { + const cookies = document.cookie.split("; "); + let val: string | undefined = undefined; + cookies.find((item) => { + const [k, v] = item.split("="); + if (k === `${this.prefix}${key}`) { + val = v; + return true; + } + return false; + }); - return val; - } + return val; + } - /** - * @description: 删除指定key的cookie - * @param {string} key 键 - * @Date: 2023-05-17 18:32:56 - * @Author: mulingyuer - */ - public removeItem(key: string): void { - this.setItem(key, "", -1); - } + /** + * @description: 删除指定key的cookie + * @param {string} key 键 + * @Date: 2023-05-17 18:32:56 + * @Author: mulingyuer + */ + public removeItem(key: string): void { + this.setItem(key, "", -1); + } - /** - * @description: 清空所有cookie - * @Date: 2023-05-17 23:13:04 - * @Author: mulingyuer - */ - public clear(): void { - const cookies = document.cookie.split("; "); - cookies.forEach((item) => { - const [k] = item.split("="); - this.removeItem(k); - }); - } + /** + * @description: 清空所有cookie + * @Date: 2023-05-17 23:13:04 + * @Author: mulingyuer + */ + public clear(): void { + const cookies = document.cookie.split("; "); + cookies.forEach((item) => { + const [k] = item.split("="); + this.removeItem(k); + }); + } } const cookieStorage = new CookieStorage(); -export default cookieStorage; -export { CookieStorage }; +export {cookieStorage}; diff --git a/src/utils/localforage/index.ts b/src/utils/localforage/index.ts index a075fab..ad86797 100644 --- a/src/utils/localforage/index.ts +++ b/src/utils/localforage/index.ts @@ -41,7 +41,7 @@ export const decrypt = (data: string | unknown) => { export const handleLocalforage = { config: async (options?: LocalForageOptions) => localforage.config(options || {}), - setItem: (key: string, value: string) => { + setItem: (key: string, value: any) => { localforage.setItem(key, encrypt(value)); }, getItem: (key: string) => { diff --git a/src/utils/nprogress/nprogress.ts b/src/utils/nprogress/nprogress.ts new file mode 100644 index 0000000..542cdfa --- /dev/null +++ b/src/utils/nprogress/nprogress.ts @@ -0,0 +1,22 @@ +import NProgress from 'nprogress'; +import 'nprogress/nprogress.css'; + +//全局进度条的配置 +NProgress.configure({ + easing: 'ease', // 动画方式 + speed: 300, // 递增进度条的速度 + showSpinner: false, // 是否显示加载ico + trickleSpeed: 200, // 自动递增间隔 + minimum: 0.3, // 更改启动时使用的最小百分比 + parent: 'body' //指定进度条的父容器 +}); + +// 打开进度条 +export const start = () => { + NProgress.start(); +}; + +// 关闭进度条 +export const close = () => { + NProgress.done(); +}; diff --git a/src/views/Login/LoginPage.vue b/src/views/Login/LoginPage.vue index 994a9d1..f76e034 100644 --- a/src/views/Login/LoginPage.vue +++ b/src/views/Login/LoginPage.vue @@ -1,15 +1,20 @@ diff --git a/vite.config.ts b/vite.config.ts index b8115ea..a7e6aa6 100644 --- a/vite.config.ts +++ b/vite.config.ts @@ -105,7 +105,7 @@ export default defineConfig(({mode}: { mode: string }): object => { assetFileNames: '[ext]/[name]-[hash].[ext]' ,// 资源文件像 字体,图片等 manualChunks(id: string){ if (id.includes('node_modules')) { - return id.toString().split('node_modules/')[1].split('/')[0].toString(); + return "vendor"; } } }, diff --git a/yarn.lock b/yarn.lock index c0537b5..be20533 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2,6 +2,18 @@ # yarn lockfile v1 +"@alova/adapter-axios@^2.0.5": + version "2.0.5" + resolved "https://registry.npmmirror.com/@alova/adapter-axios/-/adapter-axios-2.0.5.tgz#a6788e124ec74d25846519a15518902b2fa0ae1a" + integrity sha512-JTcNdg0d9nFpbwPp2RPAWGyph+HfSUQe3qCC9sg9yATGz1X2Ed0pnug1ovn1defrjWUYm+Iod2MTv1fsym5LHA== + dependencies: + "@alova/shared" "^1.0.4" + +"@alova/shared@^1.0.4": + version "1.0.4" + resolved "https://registry.npmmirror.com/@alova/shared/-/shared-1.0.4.tgz#56176e6f71dbb8b8a8dec49092277c6e0705d55a" + integrity sha512-Tq47Wd5q76kPmGLXmPijb0AfsXW2aWR9Pid1KO1nz96BdWiKstx2t/ZLTNaGtQzYyB6M+puunaTTJbusJQPmkQ== + "@ant-design/colors@^6.0.0": version "6.0.0" resolved "https://registry.npmmirror.com/@ant-design/colors/-/colors-6.0.0.tgz#9b9366257cffcc47db42b9d0203bb592c13c0298" @@ -438,6 +450,11 @@ dependencies: undici-types "~6.13.0" +"@types/nprogress@^0.2.3": + version "0.2.3" + resolved "https://registry.npmmirror.com/@types/nprogress/-/nprogress-0.2.3.tgz#b2150b054a13622fabcba12cf6f0b54c48b14287" + integrity sha512-k7kRA033QNtC+gLc4VPlfnue58CM1iQLgn1IMAU8VPHGOj7oIHPp9UlhedEnD/Gl8evoCjwkZjlBORtZ3JByUA== + "@typescript-eslint/eslint-plugin@8.0.1": version "8.0.1" resolved "https://registry.npmmirror.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.0.1.tgz#5dbd1b498fdea83a16d292322d27d293ce156f94" @@ -672,6 +689,14 @@ ajv@^6.12.4: json-schema-traverse "^0.4.1" uri-js "^4.2.2" +alova@^3.0.5: + version "3.0.5" + resolved "https://registry.npmmirror.com/alova/-/alova-3.0.5.tgz#876bd7108d5fecf19de1425e59f695f4769933fe" + integrity sha512-cOE2nTPOp7sXLhf9cthdh90lT389C1akgJULMytuFeV1loriJr1YbT3LCw3qb/P3N+o/QtJ9WLH2ccr0vJ380A== + dependencies: + "@alova/shared" "^1.0.4" + rate-limiter-flexible "^5.0.3" + ansi-regex@^5.0.1: version "5.0.1" resolved "https://registry.npmmirror.com/ansi-regex/-/ansi-regex-5.0.1.tgz#082cb2c89c9fe8659a311a53bd6a4dc5301db304" @@ -2304,6 +2329,11 @@ normalize-path@^3.0.0, normalize-path@~3.0.0: resolved "https://registry.npmmirror.com/normalize-path/-/normalize-path-3.0.0.tgz#0dcd69ff23a1c9b11fd0978316644a0388216a65" integrity sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA== +nprogress@^0.2.0: + version "0.2.0" + resolved "https://registry.npmmirror.com/nprogress/-/nprogress-0.2.0.tgz#cb8f34c53213d895723fcbab907e9422adbcafb1" + integrity sha512-I19aIingLgR1fmhftnbWWO3dXc0hSxqHQHQb3H8m+K3TnEn/iSeTZZOyvKXWqQESMwuUVnatlCnZdLBZZt2VSA== + nth-check@^2.0.1, nth-check@^2.1.1: version "2.1.1" resolved "https://registry.npmmirror.com/nth-check/-/nth-check-2.1.1.tgz#c9eab428effce36cd6b92c924bdb000ef1f1ed1d" @@ -2606,6 +2636,11 @@ randomfill@^1.0.3: randombytes "^2.0.5" safe-buffer "^5.1.0" +rate-limiter-flexible@^5.0.3: + version "5.0.3" + resolved "https://registry.npmmirror.com/rate-limiter-flexible/-/rate-limiter-flexible-5.0.3.tgz#bfbfd7585e09073ebe22d177126116862b1024ae" + integrity sha512-lWx2y8NBVlTOLPyqs+6y7dxfEpT6YFqKy3MzWbCy95sTTOhOuxufP2QvRyOHpfXpB9OUJPbVLybw3z3AVAS5fA== + readable-stream@^2.3.8: version "2.3.8" resolved "https://registry.npmmirror.com/readable-stream/-/readable-stream-2.3.8.tgz#91125e8042bba1b9887f49345f6277027ce8be9b"