├── README.md ├── client ├── __VLS_types.d.ts ├── index.html ├── package-lock.json ├── package.json ├── src │ ├── App.vue │ ├── axios.ts │ ├── components │ │ ├── Login.vue │ │ └── Register.vue │ ├── main.ts │ ├── router │ │ └── index.ts │ ├── shims-vue.d.ts │ ├── store │ │ └── auth.ts │ ├── types │ │ └── Article.d.ts │ ├── views │ │ ├── CurrencyExchangeView.vue │ │ ├── HomeView.vue │ │ ├── NewsDetailView.vue │ │ └── NewsView.vue │ └── vite-env.d.ts ├── tsconfig.json ├── tsconfig.node.json └── vite.config.ts ├── image-1.png ├── image-3.png ├── image-4.png ├── image-5.png ├── image.png ├── server ├── package-lock.json ├── package.json ├── src │ ├── app.ts │ ├── controllers │ │ ├── articleController.ts │ │ ├── authController.ts │ │ └── exchangeRateController.ts │ ├── index.ts │ ├── middlewares │ │ └── authMiddleware.ts │ ├── models │ │ ├── Article.ts │ │ ├── ExchangeRate.ts │ │ └── User.ts │ └── routes │ │ ├── article.ts │ │ ├── auth.ts │ │ └── exchangeRate.ts └── tsconfig.json ├── vue感情参考書(课件).md ├── 前端代码说明.md └── 后端配置说明.md /README.md: -------------------------------------------------------------------------------- 1 | # README.md 2 | 3 | * Код и текстовая версия учебника по фреймворку Vue, Bilibili, InkkaPlumChannel. 4 | 5 | 这是[B站InkkaPlum频道](https://space.bilibili.com/290859233), 知乎[Inkka Plum](https://www.zhihu.com/people/instead-opt)的Vue教程对应的综合练习项目源码, 具体教程/操作方法请查看文件[后端配置说明.md](后端配置说明.md)及[前端代码说明.md](前端代码说明.md) 6 | 7 | 反馈问题: 直接在B站/知乎私信即可。 8 | 9 | 和Up交流: 直接在B站/知乎私信即可。 10 | 11 | 注意: 不得二次用于任何机构/个人再次录制 Vue/Node/Element 或其它任何语言, 框架, 架构, 工具等等教程中。但是非常欢迎修改项目或者添加更多功能。这个项目只是实现了基本的功能, 也有很多未完成的点。 12 | 13 | 以上就是全部内容, 如果有任何问题, 欢迎私信UP主反馈! 14 | 15 | 以上 祝学习成功! 16 | 17 | Inkka Plum 18 | -------------------------------------------------------------------------------- /client/__VLS_types.d.ts: -------------------------------------------------------------------------------- 1 | // @ts-nocheck 2 | 3 | type __VLS_IntrinsicElements = __VLS_PickNotAny>>; 4 | type __VLS_Element = __VLS_PickNotAny; 5 | 6 | type __VLS_IsAny = 0 extends 1 & T ? true : false; 7 | type __VLS_PickNotAny = __VLS_IsAny extends true ? B : A; 8 | 9 | type __VLS_Prettify = { [K in keyof T]: T[K]; } & {}; 10 | 11 | type __VLS_OmitKeepDiscriminatedUnion = 12 | T extends any 13 | ? Pick> 14 | : never; 15 | 16 | type __VLS_GlobalComponents = 17 | __VLS_PickNotAny 18 | & __VLS_PickNotAny 19 | & __VLS_PickNotAny 20 | & Pick; 27 | 28 | declare const __VLS_intrinsicElements: __VLS_IntrinsicElements; 29 | 30 | // v-for 31 | declare function __VLS_getVForSourceType(source: number): [number, number, number][]; 32 | declare function __VLS_getVForSourceType(source: string): [string, number, number][]; 33 | declare function __VLS_getVForSourceType(source: T): [ 34 | T[number], // item 35 | number, // key 36 | number, // index 37 | ][]; 38 | declare function __VLS_getVForSourceType }>(source: T): [ 39 | T extends { [Symbol.iterator](): Iterator } ? T1 : never, // item 40 | number, // key 41 | undefined, // index 42 | ][]; 43 | declare function __VLS_getVForSourceType(source: T): [ 44 | T[keyof T], // item 45 | keyof T, // key 46 | number, // index 47 | ][]; 48 | 49 | declare function __VLS_getSlotParams(slot: T): Parameters<__VLS_PickNotAny, (...args: any[]) => any>>; 50 | declare function __VLS_getSlotParam(slot: T): Parameters<__VLS_PickNotAny, (...args: any[]) => any>>[0]; 51 | declare function __VLS_directiveFunction(dir: T): 52 | T extends import('vue').ObjectDirective | import('vue').FunctionDirective ? (value: V) => void 53 | : T; 54 | declare function __VLS_withScope(ctx: T, scope: K): ctx is T & K; 55 | declare function __VLS_makeOptional(t: T): { [K in keyof T]?: T[K] }; 56 | 57 | type __VLS_SelfComponent = string extends N ? {} : N extends string ? { [P in N]: C } : {}; 58 | type __VLS_WithComponent = 59 | N1 extends keyof LocalComponents ? N1 extends N0 ? Pick : { [K in N0]: LocalComponents[N1] } : 60 | N2 extends keyof LocalComponents ? N2 extends N0 ? Pick : { [K in N0]: LocalComponents[N2] } : 61 | N3 extends keyof LocalComponents ? N3 extends N0 ? Pick : { [K in N0]: LocalComponents[N3] } : 62 | N1 extends keyof __VLS_GlobalComponents ? N1 extends N0 ? Pick<__VLS_GlobalComponents, N0> : { [K in N0]: __VLS_GlobalComponents[N1] } : 63 | N2 extends keyof __VLS_GlobalComponents ? N2 extends N0 ? Pick<__VLS_GlobalComponents, N0> : { [K in N0]: __VLS_GlobalComponents[N2] } : 64 | N3 extends keyof __VLS_GlobalComponents ? N3 extends N0 ? Pick<__VLS_GlobalComponents, N0> : { [K in N0]: __VLS_GlobalComponents[N3] } : 65 | { [K in N0]: unknown } 66 | 67 | type __VLS_FillingEventArg_ParametersLength any> = __VLS_IsAny> extends true ? -1 : Parameters['length']; 68 | type __VLS_FillingEventArg = E extends (...args: any) => any ? __VLS_FillingEventArg_ParametersLength extends 0 ? ($event?: undefined) => ReturnType : E : E; 69 | declare function __VLS_asFunctionalComponent any ? InstanceType : unknown>(t: T, instance?: K): 70 | T extends new (...args: any) => any 71 | ? (props: (K extends { $props: infer Props } ? Props : any) & Record, ctx?: { 72 | attrs?: any, 73 | slots?: K extends { $slots: infer Slots } ? Slots : any, 74 | emit?: K extends { $emit: infer Emit } ? Emit : any 75 | }) => __VLS_Element & { __ctx?: typeof ctx & { props?: typeof props; expose?(exposed: K): void; } } 76 | : T extends () => any ? (props: {}, ctx?: any) => ReturnType 77 | : T extends (...args: any) => any ? T 78 | : (_: {} & Record, ctx?: any) => { __ctx?: { attrs?: any, expose?: any, slots?: any, emit?: any, props?: {} & Record } }; 79 | declare function __VLS_elementAsFunctionalComponent(t: T): (_: T & Record, ctx?: any) => { __ctx?: { attrs?: any, expose?: any, slots?: any, emit?: any, props?: T & Record } }; 80 | declare function __VLS_functionalComponentArgsRest any>(t: T): Parameters['length'] extends 2 ? [any] : []; 81 | declare function __VLS_pickEvent(emitEvent: E1, propEvent: E2): __VLS_FillingEventArg< 82 | __VLS_PickNotAny< 83 | __VLS_AsFunctionOrAny, 84 | __VLS_AsFunctionOrAny 85 | > 86 | > | undefined; 87 | declare function __VLS_pickFunctionalComponentCtx(comp: T, compInstance: K): __VLS_PickNotAny< 88 | '__ctx' extends keyof __VLS_PickNotAny ? K extends { __ctx?: infer Ctx } ? Ctx : never : any 89 | , T extends (props: any, ctx: infer Ctx) => any ? Ctx : any 90 | >; 91 | type __VLS_FunctionalComponentProps = 92 | '__ctx' extends keyof __VLS_PickNotAny ? K extends { __ctx?: { props?: infer P } } ? NonNullable

: never 93 | : T extends (props: infer P, ...args: any) => any ? P : 94 | {}; 95 | type __VLS_AsFunctionOrAny = unknown extends F ? any : ((...args: any) => any) extends F ? F : any; 96 | 97 | declare function __VLS_normalizeSlot(s: S): S extends () => infer R ? (props: {}) => R : S; 98 | 99 | /** 100 | * emit 101 | */ 102 | // fix https://github.com/vuejs/language-tools/issues/926 103 | type __VLS_UnionToIntersection = (U extends unknown ? (arg: U) => unknown : never) extends ((arg: infer P) => unknown) ? P : never; 104 | type __VLS_OverloadUnionInner = U & T extends (...args: infer A) => infer R 105 | ? U extends T 106 | ? never 107 | : __VLS_OverloadUnionInner & U & ((...args: A) => R)> | ((...args: A) => R) 108 | : never; 109 | type __VLS_OverloadUnion = Exclude< 110 | __VLS_OverloadUnionInner<(() => never) & T>, 111 | T extends () => never ? never : () => never 112 | >; 113 | type __VLS_ConstructorOverloads = __VLS_OverloadUnion extends infer F 114 | ? F extends (event: infer E, ...args: infer A) => any 115 | ? { [K in E & string]: (...args: A) => void; } 116 | : never 117 | : never; 118 | type __VLS_NormalizeEmits = __VLS_Prettify< 119 | __VLS_UnionToIntersection< 120 | __VLS_ConstructorOverloads & { 121 | [K in keyof T]: T[K] extends any[] ? { (...args: T[K]): void } : never 122 | } 123 | > 124 | >; -------------------------------------------------------------------------------- /client/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | ExchangeApp 8 | 9 | 10 |

11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /client/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "exchangeappdemo", 3 | "private": true, 4 | "version": "0.0.0", 5 | "type": "module", 6 | "scripts": { 7 | "dev": "vite", 8 | "build": "vue-tsc && vite build", 9 | "preview": "vite preview" 10 | }, 11 | "dependencies": { 12 | "axios": "^1.7.2", 13 | "element-plus": "^2.7.4", 14 | "pinia": "^2.1.7", 15 | "router": "^1.3.8", 16 | "vant": "^4.9.0", 17 | "vue": "^3.4.21", 18 | "vue-router": "^4.3.2" 19 | }, 20 | "devDependencies": { 21 | "@types/node": "^20.12.13", 22 | "@types/vue-router": "^2.0.0", 23 | "@typescript-eslint/eslint-plugin": "^7.11.0", 24 | "@typescript-eslint/parser": "^7.11.0", 25 | "@vitejs/plugin-vue": "^5.0.4", 26 | "@vue/cli-plugin-typescript": "^5.0.8", 27 | "@vue/eslint-config-typescript": "^13.0.0", 28 | "typescript": "^5.2.2", 29 | "vite": "^5.2.0", 30 | "vue-tsc": "^2.0.6" 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /client/src/App.vue: -------------------------------------------------------------------------------- 1 | 18 | 19 | 42 | 43 | -------------------------------------------------------------------------------- /client/src/axios.ts: -------------------------------------------------------------------------------- 1 | import axios from 'axios'; 2 | 3 | const instance = axios.create({ 4 | baseURL: 'http://localhost:3000/api', 5 | }); 6 | 7 | instance.interceptors.request.use(config => { 8 | const token = localStorage.getItem('token'); 9 | if (token) { 10 | config.headers.Authorization = "Bearer " + token; 11 | } 12 | return config; 13 | }); 14 | 15 | export default instance; 16 | -------------------------------------------------------------------------------- /client/src/components/Login.vue: -------------------------------------------------------------------------------- 1 | 16 | 17 | 40 | 41 | 61 | -------------------------------------------------------------------------------- /client/src/components/Register.vue: -------------------------------------------------------------------------------- 1 | 14 | 15 | 38 | 39 | 59 | -------------------------------------------------------------------------------- /client/src/main.ts: -------------------------------------------------------------------------------- 1 | import { createApp } from 'vue'; 2 | import { createPinia } from 'pinia'; 3 | import ElementPlus from 'element-plus'; 4 | import 'element-plus/dist/index.css'; 5 | import App from './App.vue'; 6 | import router from './router'; 7 | 8 | const app = createApp(App); 9 | app.use(createPinia()); 10 | app.use(ElementPlus); 11 | app.use(router); 12 | app.mount('#app'); 13 | -------------------------------------------------------------------------------- /client/src/router/index.ts: -------------------------------------------------------------------------------- 1 | import { createRouter, createWebHistory, RouteRecordRaw } from 'vue-router'; 2 | import HomeView from '../views/HomeView.vue'; 3 | import CurrencyExchangeView from '../views/CurrencyExchangeView.vue'; 4 | import NewsView from '../views/NewsView.vue'; 5 | import NewsDetailView from '../views/NewsDetailView.vue'; 6 | import Login from '../components/Login.vue'; 7 | import Register from '../components/Register.vue'; 8 | 9 | const routes: RouteRecordRaw[] = [ 10 | { path: '/', name: 'Home', component: HomeView }, 11 | { path: '/exchange', name: 'CurrencyExchange', component: CurrencyExchangeView }, 12 | { path: '/news', name: 'News', component: NewsView }, 13 | { path: '/news/:id', name: 'NewsDetail', component: NewsDetailView }, 14 | { path: '/login', name: 'Login', component: Login }, 15 | { path: '/register', name: 'Register', component: Register }, 16 | ]; 17 | 18 | const router = createRouter({ 19 | history: createWebHistory(), 20 | routes, 21 | }); 22 | 23 | export default router; 24 | -------------------------------------------------------------------------------- /client/src/shims-vue.d.ts: -------------------------------------------------------------------------------- 1 | // client/src/shims-vue.d.ts 2 | declare module '*.vue' { 3 | import { DefineComponent } from 'vue'; 4 | const component: DefineComponent<{}, {}, any>; 5 | export default component; 6 | } 7 | -------------------------------------------------------------------------------- /client/src/store/auth.ts: -------------------------------------------------------------------------------- 1 | import { defineStore } from 'pinia'; 2 | import { ref, computed } from 'vue'; 3 | import axios from '../axios'; 4 | 5 | export const useAuthStore = defineStore('auth', () => { 6 | const token = ref(localStorage.getItem('token')); 7 | 8 | const isAuthenticated = computed(() => !!token.value); 9 | 10 | const login = async (username: string, password: string) => { 11 | try { 12 | const response = await axios.post('/auth/login', { username, password }); 13 | token.value = response.data.token; 14 | localStorage.setItem('token', token.value || ''); 15 | } catch (error) { 16 | console.error('Login error:', error); 17 | } 18 | }; 19 | 20 | const register = async (username: string, password: string) => { 21 | try { 22 | const response = await axios.post('/auth/register', { username, password }); 23 | token.value = response.data.token; 24 | localStorage.setItem('token', token.value || ''); 25 | } catch (error) { 26 | console.error('Register error:', error); 27 | } 28 | }; 29 | 30 | const logout = () => { 31 | token.value = null; 32 | localStorage.removeItem('token'); 33 | }; 34 | 35 | return { 36 | token, 37 | isAuthenticated, 38 | login, 39 | register, 40 | logout 41 | }; 42 | }); 43 | -------------------------------------------------------------------------------- /client/src/types/Article.d.ts: -------------------------------------------------------------------------------- 1 | export interface Article { 2 | _id: string; 3 | title: string; 4 | preview: string; 5 | content: string; 6 | } -------------------------------------------------------------------------------- /client/src/views/CurrencyExchangeView.vue: -------------------------------------------------------------------------------- 1 | 26 | 27 | 71 | 72 | 92 | -------------------------------------------------------------------------------- /client/src/views/HomeView.vue: -------------------------------------------------------------------------------- 1 | 9 | 10 | 12 | 13 | -------------------------------------------------------------------------------- /client/src/views/NewsDetailView.vue: -------------------------------------------------------------------------------- 1 | 12 | 13 | 34 | 35 | 46 | 47 | -------------------------------------------------------------------------------- /client/src/views/NewsView.vue: -------------------------------------------------------------------------------- 1 | 15 | 16 | 47 | 48 | 59 | -------------------------------------------------------------------------------- /client/src/vite-env.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | -------------------------------------------------------------------------------- /client/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "esnext", 4 | "module": "esnext", 5 | "strict": true, 6 | "jsx": "preserve", 7 | "moduleResolution": "node", 8 | "resolveJsonModule": true, 9 | "esModuleInterop": true, 10 | "skipLibCheck": true, 11 | "forceConsistentCasingInFileNames": true, 12 | "baseUrl": ".", 13 | "paths": { 14 | "@/*": ["src/*"] 15 | }, 16 | "types": ["node", "vite/client"] 17 | }, 18 | "include": ["src/**/*.ts", "src/**/*.d.ts", "src/**/*.tsx", "src/**/*.vue"] 19 | } 20 | -------------------------------------------------------------------------------- /client/tsconfig.node.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "composite": true, 4 | "skipLibCheck": true, 5 | "module": "ESNext", 6 | "moduleResolution": "bundler", 7 | "allowSyntheticDefaultImports": true, 8 | "strict": true 9 | }, 10 | "include": ["vite.config.ts"] 11 | } 12 | -------------------------------------------------------------------------------- /client/vite.config.ts: -------------------------------------------------------------------------------- 1 | // https://vitejs.dev/config/ 2 | import { defineConfig } from 'vite'; 3 | import vue from '@vitejs/plugin-vue'; 4 | 5 | export default defineConfig({ 6 | plugins: [vue()], 7 | server: { 8 | proxy: { 9 | '/api': { 10 | target: 'http://localhost:3000', 11 | changeOrigin: true, 12 | secure: false, 13 | rewrite: (path) => path.replace(/^\/api/, '') 14 | } 15 | } 16 | } 17 | }); 18 | -------------------------------------------------------------------------------- /image-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Slumhee/Web002VUE-01_vuebasictutorials/02875cadaafafd3f8e1bd04c756c8695f71c1613/image-1.png -------------------------------------------------------------------------------- /image-3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Slumhee/Web002VUE-01_vuebasictutorials/02875cadaafafd3f8e1bd04c756c8695f71c1613/image-3.png -------------------------------------------------------------------------------- /image-4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Slumhee/Web002VUE-01_vuebasictutorials/02875cadaafafd3f8e1bd04c756c8695f71c1613/image-4.png -------------------------------------------------------------------------------- /image-5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Slumhee/Web002VUE-01_vuebasictutorials/02875cadaafafd3f8e1bd04c756c8695f71c1613/image-5.png -------------------------------------------------------------------------------- /image.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Slumhee/Web002VUE-01_vuebasictutorials/02875cadaafafd3f8e1bd04c756c8695f71c1613/image.png -------------------------------------------------------------------------------- /server/package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "server", 3 | "version": "1.0.0", 4 | "lockfileVersion": 3, 5 | "requires": true, 6 | "packages": { 7 | "": { 8 | "name": "server", 9 | "version": "1.0.0", 10 | "dependencies": { 11 | "bcryptjs": "^2.4.3", 12 | "cors": "^2.8.5", 13 | "express": "^4.17.1", 14 | "jsonwebtoken": "^8.5.1", 15 | "mongoose": "^5.13.5" 16 | }, 17 | "devDependencies": { 18 | "@types/bcryptjs": "^2.4.2", 19 | "@types/cors": "^2.8.17", 20 | "@types/express": "^4.17.13", 21 | "@types/jsonwebtoken": "^8.5.0", 22 | "@types/mongoose": "^5.11.97", 23 | "@types/node": "^16.7.13", 24 | "ts-node": "^10.2.1", 25 | "ts-node-dev": "^1.1.6", 26 | "typescript": "^5.4.5" 27 | } 28 | }, 29 | "node_modules/@cspotcode/source-map-support": { 30 | "version": "0.8.1", 31 | "resolved": "https://registry.npmmirror.com/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz", 32 | "integrity": "sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==", 33 | "dev": true, 34 | "dependencies": { 35 | "@jridgewell/trace-mapping": "0.3.9" 36 | }, 37 | "engines": { 38 | "node": ">=12" 39 | } 40 | }, 41 | "node_modules/@jridgewell/resolve-uri": { 42 | "version": "3.1.2", 43 | "resolved": "https://registry.npmmirror.com/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", 44 | "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", 45 | "dev": true, 46 | "engines": { 47 | "node": ">=6.0.0" 48 | } 49 | }, 50 | "node_modules/@jridgewell/sourcemap-codec": { 51 | "version": "1.4.15", 52 | "resolved": "https://registry.npmmirror.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz", 53 | "integrity": "sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==", 54 | "dev": true 55 | }, 56 | "node_modules/@jridgewell/trace-mapping": { 57 | "version": "0.3.9", 58 | "resolved": "https://registry.npmmirror.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz", 59 | "integrity": "sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==", 60 | "dev": true, 61 | "dependencies": { 62 | "@jridgewell/resolve-uri": "^3.0.3", 63 | "@jridgewell/sourcemap-codec": "^1.4.10" 64 | } 65 | }, 66 | "node_modules/@tsconfig/node10": { 67 | "version": "1.0.11", 68 | "resolved": "https://registry.npmmirror.com/@tsconfig/node10/-/node10-1.0.11.tgz", 69 | "integrity": "sha512-DcRjDCujK/kCk/cUe8Xz8ZSpm8mS3mNNpta+jGCA6USEDfktlNvm1+IuZ9eTcDbNk41BHwpHHeW+N1lKCz4zOw==", 70 | "dev": true 71 | }, 72 | "node_modules/@tsconfig/node12": { 73 | "version": "1.0.11", 74 | "resolved": "https://registry.npmmirror.com/@tsconfig/node12/-/node12-1.0.11.tgz", 75 | "integrity": "sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==", 76 | "dev": true 77 | }, 78 | "node_modules/@tsconfig/node14": { 79 | "version": "1.0.3", 80 | "resolved": "https://registry.npmmirror.com/@tsconfig/node14/-/node14-1.0.3.tgz", 81 | "integrity": "sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==", 82 | "dev": true 83 | }, 84 | "node_modules/@tsconfig/node16": { 85 | "version": "1.0.4", 86 | "resolved": "https://registry.npmmirror.com/@tsconfig/node16/-/node16-1.0.4.tgz", 87 | "integrity": "sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA==", 88 | "dev": true 89 | }, 90 | "node_modules/@types/bcryptjs": { 91 | "version": "2.4.6", 92 | "resolved": "https://registry.npmmirror.com/@types/bcryptjs/-/bcryptjs-2.4.6.tgz", 93 | "integrity": "sha512-9xlo6R2qDs5uixm0bcIqCeMCE6HiQsIyel9KQySStiyqNl2tnj2mP3DX1Nf56MD6KMenNNlBBsy3LJ7gUEQPXQ==", 94 | "dev": true 95 | }, 96 | "node_modules/@types/body-parser": { 97 | "version": "1.19.5", 98 | "resolved": "https://registry.npmmirror.com/@types/body-parser/-/body-parser-1.19.5.tgz", 99 | "integrity": "sha512-fB3Zu92ucau0iQ0JMCFQE7b/dv8Ot07NI3KaZIkIUNXq82k4eBAqUaneXfleGY9JWskeS9y+u0nXMyspcuQrCg==", 100 | "dev": true, 101 | "dependencies": { 102 | "@types/connect": "*", 103 | "@types/node": "*" 104 | } 105 | }, 106 | "node_modules/@types/bson": { 107 | "version": "4.0.5", 108 | "resolved": "https://registry.npmmirror.com/@types/bson/-/bson-4.0.5.tgz", 109 | "integrity": "sha512-vVLwMUqhYJSQ/WKcE60eFqcyuWse5fGH+NMAXHuKrUAPoryq3ATxk5o4bgYNtg5aOM4APVg7Hnb3ASqUYG0PKg==", 110 | "dependencies": { 111 | "@types/node": "*" 112 | } 113 | }, 114 | "node_modules/@types/connect": { 115 | "version": "3.4.38", 116 | "resolved": "https://registry.npmmirror.com/@types/connect/-/connect-3.4.38.tgz", 117 | "integrity": "sha512-K6uROf1LD88uDQqJCktA4yzL1YYAK6NgfsI0v/mTgyPKWsX1CnJ0XPSDhViejru1GcRkLWb8RlzFYJRqGUbaug==", 118 | "dev": true, 119 | "dependencies": { 120 | "@types/node": "*" 121 | } 122 | }, 123 | "node_modules/@types/cors": { 124 | "version": "2.8.17", 125 | "resolved": "https://registry.npmmirror.com/@types/cors/-/cors-2.8.17.tgz", 126 | "integrity": "sha512-8CGDvrBj1zgo2qE+oS3pOCyYNqCPryMWY2bGfwA0dcfopWGgxs+78df0Rs3rc9THP4JkOhLsAa+15VdpAqkcUA==", 127 | "dev": true, 128 | "dependencies": { 129 | "@types/node": "*" 130 | } 131 | }, 132 | "node_modules/@types/express": { 133 | "version": "4.17.21", 134 | "resolved": "https://registry.npmmirror.com/@types/express/-/express-4.17.21.tgz", 135 | "integrity": "sha512-ejlPM315qwLpaQlQDTjPdsUFSc6ZsP4AN6AlWnogPjQ7CVi7PYF3YVz+CY3jE2pwYf7E/7HlDAN0rV2GxTG0HQ==", 136 | "dev": true, 137 | "dependencies": { 138 | "@types/body-parser": "*", 139 | "@types/express-serve-static-core": "^4.17.33", 140 | "@types/qs": "*", 141 | "@types/serve-static": "*" 142 | } 143 | }, 144 | "node_modules/@types/express-serve-static-core": { 145 | "version": "4.19.3", 146 | "resolved": "https://registry.npmmirror.com/@types/express-serve-static-core/-/express-serve-static-core-4.19.3.tgz", 147 | "integrity": "sha512-KOzM7MhcBFlmnlr/fzISFF5vGWVSvN6fTd4T+ExOt08bA/dA5kpSzY52nMsI1KDFmUREpJelPYyuslLRSjjgCg==", 148 | "dev": true, 149 | "dependencies": { 150 | "@types/node": "*", 151 | "@types/qs": "*", 152 | "@types/range-parser": "*", 153 | "@types/send": "*" 154 | } 155 | }, 156 | "node_modules/@types/http-errors": { 157 | "version": "2.0.4", 158 | "resolved": "https://registry.npmmirror.com/@types/http-errors/-/http-errors-2.0.4.tgz", 159 | "integrity": "sha512-D0CFMMtydbJAegzOyHjtiKPLlvnm3iTZyZRSZoLq2mRhDdmLfIWOCYPfQJ4cu2erKghU++QvjcUjp/5h7hESpA==", 160 | "dev": true 161 | }, 162 | "node_modules/@types/jsonwebtoken": { 163 | "version": "8.5.9", 164 | "resolved": "https://registry.npmmirror.com/@types/jsonwebtoken/-/jsonwebtoken-8.5.9.tgz", 165 | "integrity": "sha512-272FMnFGzAVMGtu9tkr29hRL6bZj4Zs1KZNeHLnKqAvp06tAIcarTMwOh8/8bz4FmKRcMxZhZNeUAQsNLoiPhg==", 166 | "dev": true, 167 | "dependencies": { 168 | "@types/node": "*" 169 | } 170 | }, 171 | "node_modules/@types/mime": { 172 | "version": "1.3.5", 173 | "resolved": "https://registry.npmmirror.com/@types/mime/-/mime-1.3.5.tgz", 174 | "integrity": "sha512-/pyBZWSLD2n0dcHE3hq8s8ZvcETHtEuF+3E7XVt0Ig2nvsVQXdghHVcEkIWjy9A0wKfTn97a/PSDYohKIlnP/w==", 175 | "dev": true 176 | }, 177 | "node_modules/@types/mongodb": { 178 | "version": "3.6.20", 179 | "resolved": "https://registry.npmmirror.com/@types/mongodb/-/mongodb-3.6.20.tgz", 180 | "integrity": "sha512-WcdpPJCakFzcWWD9juKoZbRtQxKIMYF/JIAM4JrNHrMcnJL6/a2NWjXxW7fo9hxboxxkg+icff8d7+WIEvKgYQ==", 181 | "dependencies": { 182 | "@types/bson": "*", 183 | "@types/node": "*" 184 | } 185 | }, 186 | "node_modules/@types/mongoose": { 187 | "version": "5.11.97", 188 | "resolved": "https://registry.npmmirror.com/@types/mongoose/-/mongoose-5.11.97.tgz", 189 | "integrity": "sha512-cqwOVYT3qXyLiGw7ueU2kX9noE8DPGRY6z8eUxudhXY8NZ7DMKYAxyZkLSevGfhCX3dO/AoX5/SO9lAzfjon0Q==", 190 | "deprecated": "Mongoose publishes its own types, so you do not need to install this package.", 191 | "dev": true, 192 | "dependencies": { 193 | "mongoose": "*" 194 | } 195 | }, 196 | "node_modules/@types/node": { 197 | "version": "16.18.97", 198 | "resolved": "https://registry.npmmirror.com/@types/node/-/node-16.18.97.tgz", 199 | "integrity": "sha512-4muilE1Lbfn57unR+/nT9AFjWk0MtWi5muwCEJqnOvfRQDbSfLCUdN7vCIg8TYuaANfhLOV85ve+FNpiUsbSRg==" 200 | }, 201 | "node_modules/@types/qs": { 202 | "version": "6.9.15", 203 | "resolved": "https://registry.npmmirror.com/@types/qs/-/qs-6.9.15.tgz", 204 | "integrity": "sha512-uXHQKES6DQKKCLh441Xv/dwxOq1TVS3JPUMlEqoEglvlhR6Mxnlew/Xq/LRVHpLyk7iK3zODe1qYHIMltO7XGg==", 205 | "dev": true 206 | }, 207 | "node_modules/@types/range-parser": { 208 | "version": "1.2.7", 209 | "resolved": "https://registry.npmmirror.com/@types/range-parser/-/range-parser-1.2.7.tgz", 210 | "integrity": "sha512-hKormJbkJqzQGhziax5PItDUTMAM9uE2XXQmM37dyd4hVM+5aVl7oVxMVUiVQn2oCQFN/LKCZdvSM0pFRqbSmQ==", 211 | "dev": true 212 | }, 213 | "node_modules/@types/send": { 214 | "version": "0.17.4", 215 | "resolved": "https://registry.npmmirror.com/@types/send/-/send-0.17.4.tgz", 216 | "integrity": "sha512-x2EM6TJOybec7c52BX0ZspPodMsQUd5L6PRwOunVyVUhXiBSKf3AezDL8Dgvgt5o0UfKNfuA0eMLr2wLT4AiBA==", 217 | "dev": true, 218 | "dependencies": { 219 | "@types/mime": "^1", 220 | "@types/node": "*" 221 | } 222 | }, 223 | "node_modules/@types/serve-static": { 224 | "version": "1.15.7", 225 | "resolved": "https://registry.npmmirror.com/@types/serve-static/-/serve-static-1.15.7.tgz", 226 | "integrity": "sha512-W8Ym+h8nhuRwaKPaDw34QUkwsGi6Rc4yYqvKFo5rm2FUEhCFbzVWrxXUxuKK8TASjWsysJY0nsmNCGhCOIsrOw==", 227 | "dev": true, 228 | "dependencies": { 229 | "@types/http-errors": "*", 230 | "@types/node": "*", 231 | "@types/send": "*" 232 | } 233 | }, 234 | "node_modules/@types/strip-bom": { 235 | "version": "3.0.0", 236 | "resolved": "https://registry.npmmirror.com/@types/strip-bom/-/strip-bom-3.0.0.tgz", 237 | "integrity": "sha512-xevGOReSYGM7g/kUBZzPqCrR/KYAo+F0yiPc85WFTJa0MSLtyFTVTU6cJu/aV4mid7IffDIWqo69THF2o4JiEQ==", 238 | "dev": true 239 | }, 240 | "node_modules/@types/strip-json-comments": { 241 | "version": "0.0.30", 242 | "resolved": "https://registry.npmmirror.com/@types/strip-json-comments/-/strip-json-comments-0.0.30.tgz", 243 | "integrity": "sha512-7NQmHra/JILCd1QqpSzl8+mJRc8ZHz3uDm8YV1Ks9IhK0epEiTw8aIErbvH9PI+6XbqhyIQy3462nEsn7UVzjQ==", 244 | "dev": true 245 | }, 246 | "node_modules/accepts": { 247 | "version": "1.3.8", 248 | "resolved": "https://registry.npmmirror.com/accepts/-/accepts-1.3.8.tgz", 249 | "integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==", 250 | "dependencies": { 251 | "mime-types": "~2.1.34", 252 | "negotiator": "0.6.3" 253 | }, 254 | "engines": { 255 | "node": ">= 0.6" 256 | } 257 | }, 258 | "node_modules/acorn": { 259 | "version": "8.11.3", 260 | "resolved": "https://registry.npmmirror.com/acorn/-/acorn-8.11.3.tgz", 261 | "integrity": "sha512-Y9rRfJG5jcKOE0CLisYbojUjIrIEE7AGMzA/Sm4BslANhbS+cDMpgBdcPT91oJ7OuJ9hYJBx59RjbhxVnrF8Xg==", 262 | "dev": true, 263 | "bin": { 264 | "acorn": "bin/acorn" 265 | }, 266 | "engines": { 267 | "node": ">=0.4.0" 268 | } 269 | }, 270 | "node_modules/acorn-walk": { 271 | "version": "8.3.2", 272 | "resolved": "https://registry.npmmirror.com/acorn-walk/-/acorn-walk-8.3.2.tgz", 273 | "integrity": "sha512-cjkyv4OtNCIeqhHrfS81QWXoCBPExR/J62oyEqepVw8WaQeSqpW2uhuLPh1m9eWhDuOo/jUXVTlifvesOWp/4A==", 274 | "dev": true, 275 | "engines": { 276 | "node": ">=0.4.0" 277 | } 278 | }, 279 | "node_modules/anymatch": { 280 | "version": "3.1.3", 281 | "resolved": "https://registry.npmmirror.com/anymatch/-/anymatch-3.1.3.tgz", 282 | "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", 283 | "dev": true, 284 | "dependencies": { 285 | "normalize-path": "^3.0.0", 286 | "picomatch": "^2.0.4" 287 | }, 288 | "engines": { 289 | "node": ">= 8" 290 | } 291 | }, 292 | "node_modules/arg": { 293 | "version": "4.1.3", 294 | "resolved": "https://registry.npmmirror.com/arg/-/arg-4.1.3.tgz", 295 | "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==", 296 | "dev": true 297 | }, 298 | "node_modules/array-flatten": { 299 | "version": "1.1.1", 300 | "resolved": "https://registry.npmmirror.com/array-flatten/-/array-flatten-1.1.1.tgz", 301 | "integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==" 302 | }, 303 | "node_modules/balanced-match": { 304 | "version": "1.0.2", 305 | "resolved": "https://registry.npmmirror.com/balanced-match/-/balanced-match-1.0.2.tgz", 306 | "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", 307 | "dev": true 308 | }, 309 | "node_modules/bcryptjs": { 310 | "version": "2.4.3", 311 | "resolved": "https://registry.npmmirror.com/bcryptjs/-/bcryptjs-2.4.3.tgz", 312 | "integrity": "sha512-V/Hy/X9Vt7f3BbPJEi8BdVFMByHi+jNXrYkW3huaybV/kQ0KJg0Y6PkEMbn+zeT+i+SiKZ/HMqJGIIt4LZDqNQ==" 313 | }, 314 | "node_modules/binary-extensions": { 315 | "version": "2.3.0", 316 | "resolved": "https://registry.npmmirror.com/binary-extensions/-/binary-extensions-2.3.0.tgz", 317 | "integrity": "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==", 318 | "dev": true, 319 | "engines": { 320 | "node": ">=8" 321 | }, 322 | "funding": { 323 | "url": "https://github.com/sponsors/sindresorhus" 324 | } 325 | }, 326 | "node_modules/bl": { 327 | "version": "2.2.1", 328 | "resolved": "https://registry.npmmirror.com/bl/-/bl-2.2.1.tgz", 329 | "integrity": "sha512-6Pesp1w0DEX1N550i/uGV/TqucVL4AM/pgThFSN/Qq9si1/DF9aIHs1BxD8V/QU0HoeHO6cQRTAuYnLPKq1e4g==", 330 | "dependencies": { 331 | "readable-stream": "^2.3.5", 332 | "safe-buffer": "^5.1.1" 333 | } 334 | }, 335 | "node_modules/bluebird": { 336 | "version": "3.5.1", 337 | "resolved": "https://registry.npmmirror.com/bluebird/-/bluebird-3.5.1.tgz", 338 | "integrity": "sha512-MKiLiV+I1AA596t9w1sQJ8jkiSr5+ZKi0WKrYGUn6d1Fx+Ij4tIj+m2WMQSGczs5jZVxV339chE8iwk6F64wjA==" 339 | }, 340 | "node_modules/body-parser": { 341 | "version": "1.20.2", 342 | "resolved": "https://registry.npmmirror.com/body-parser/-/body-parser-1.20.2.tgz", 343 | "integrity": "sha512-ml9pReCu3M61kGlqoTm2umSXTlRTuGTx0bfYj+uIUKKYycG5NtSbeetV3faSU6R7ajOPw0g/J1PvK4qNy7s5bA==", 344 | "dependencies": { 345 | "bytes": "3.1.2", 346 | "content-type": "~1.0.5", 347 | "debug": "2.6.9", 348 | "depd": "2.0.0", 349 | "destroy": "1.2.0", 350 | "http-errors": "2.0.0", 351 | "iconv-lite": "0.4.24", 352 | "on-finished": "2.4.1", 353 | "qs": "6.11.0", 354 | "raw-body": "2.5.2", 355 | "type-is": "~1.6.18", 356 | "unpipe": "1.0.0" 357 | }, 358 | "engines": { 359 | "node": ">= 0.8", 360 | "npm": "1.2.8000 || >= 1.4.16" 361 | } 362 | }, 363 | "node_modules/brace-expansion": { 364 | "version": "1.1.11", 365 | "resolved": "https://registry.npmmirror.com/brace-expansion/-/brace-expansion-1.1.11.tgz", 366 | "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", 367 | "dev": true, 368 | "dependencies": { 369 | "balanced-match": "^1.0.0", 370 | "concat-map": "0.0.1" 371 | } 372 | }, 373 | "node_modules/braces": { 374 | "version": "3.0.3", 375 | "resolved": "https://registry.npmmirror.com/braces/-/braces-3.0.3.tgz", 376 | "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", 377 | "dev": true, 378 | "dependencies": { 379 | "fill-range": "^7.1.1" 380 | }, 381 | "engines": { 382 | "node": ">=8" 383 | } 384 | }, 385 | "node_modules/bson": { 386 | "version": "1.1.6", 387 | "resolved": "https://registry.npmmirror.com/bson/-/bson-1.1.6.tgz", 388 | "integrity": "sha512-EvVNVeGo4tHxwi8L6bPj3y3itEvStdwvvlojVxxbyYfoaxJ6keLgrTuKdyfEAszFK+H3olzBuafE0yoh0D1gdg==", 389 | "engines": { 390 | "node": ">=0.6.19" 391 | } 392 | }, 393 | "node_modules/buffer-equal-constant-time": { 394 | "version": "1.0.1", 395 | "resolved": "https://registry.npmmirror.com/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz", 396 | "integrity": "sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA==" 397 | }, 398 | "node_modules/buffer-from": { 399 | "version": "1.1.2", 400 | "resolved": "https://registry.npmmirror.com/buffer-from/-/buffer-from-1.1.2.tgz", 401 | "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", 402 | "dev": true 403 | }, 404 | "node_modules/bytes": { 405 | "version": "3.1.2", 406 | "resolved": "https://registry.npmmirror.com/bytes/-/bytes-3.1.2.tgz", 407 | "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", 408 | "engines": { 409 | "node": ">= 0.8" 410 | } 411 | }, 412 | "node_modules/call-bind": { 413 | "version": "1.0.7", 414 | "resolved": "https://registry.npmmirror.com/call-bind/-/call-bind-1.0.7.tgz", 415 | "integrity": "sha512-GHTSNSYICQ7scH7sZ+M2rFopRoLh8t2bLSW6BbgrtLsahOIB5iyAVJf9GjWK3cYTDaMj4XdBpM1cA6pIS0Kv2w==", 416 | "dependencies": { 417 | "es-define-property": "^1.0.0", 418 | "es-errors": "^1.3.0", 419 | "function-bind": "^1.1.2", 420 | "get-intrinsic": "^1.2.4", 421 | "set-function-length": "^1.2.1" 422 | }, 423 | "engines": { 424 | "node": ">= 0.4" 425 | }, 426 | "funding": { 427 | "url": "https://github.com/sponsors/ljharb" 428 | } 429 | }, 430 | "node_modules/chokidar": { 431 | "version": "3.6.0", 432 | "resolved": "https://registry.npmmirror.com/chokidar/-/chokidar-3.6.0.tgz", 433 | "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==", 434 | "dev": true, 435 | "dependencies": { 436 | "anymatch": "~3.1.2", 437 | "braces": "~3.0.2", 438 | "glob-parent": "~5.1.2", 439 | "is-binary-path": "~2.1.0", 440 | "is-glob": "~4.0.1", 441 | "normalize-path": "~3.0.0", 442 | "readdirp": "~3.6.0" 443 | }, 444 | "engines": { 445 | "node": ">= 8.10.0" 446 | }, 447 | "funding": { 448 | "url": "https://paulmillr.com/funding/" 449 | }, 450 | "optionalDependencies": { 451 | "fsevents": "~2.3.2" 452 | } 453 | }, 454 | "node_modules/concat-map": { 455 | "version": "0.0.1", 456 | "resolved": "https://registry.npmmirror.com/concat-map/-/concat-map-0.0.1.tgz", 457 | "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", 458 | "dev": true 459 | }, 460 | "node_modules/content-disposition": { 461 | "version": "0.5.4", 462 | "resolved": "https://registry.npmmirror.com/content-disposition/-/content-disposition-0.5.4.tgz", 463 | "integrity": "sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==", 464 | "dependencies": { 465 | "safe-buffer": "5.2.1" 466 | }, 467 | "engines": { 468 | "node": ">= 0.6" 469 | } 470 | }, 471 | "node_modules/content-type": { 472 | "version": "1.0.5", 473 | "resolved": "https://registry.npmmirror.com/content-type/-/content-type-1.0.5.tgz", 474 | "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==", 475 | "engines": { 476 | "node": ">= 0.6" 477 | } 478 | }, 479 | "node_modules/cookie": { 480 | "version": "0.6.0", 481 | "resolved": "https://registry.npmmirror.com/cookie/-/cookie-0.6.0.tgz", 482 | "integrity": "sha512-U71cyTamuh1CRNCfpGY6to28lxvNwPG4Guz/EVjgf3Jmzv0vlDp1atT9eS5dDjMYHucpHbWns6Lwf3BKz6svdw==", 483 | "engines": { 484 | "node": ">= 0.6" 485 | } 486 | }, 487 | "node_modules/cookie-signature": { 488 | "version": "1.0.6", 489 | "resolved": "https://registry.npmmirror.com/cookie-signature/-/cookie-signature-1.0.6.tgz", 490 | "integrity": "sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==" 491 | }, 492 | "node_modules/core-util-is": { 493 | "version": "1.0.3", 494 | "resolved": "https://registry.npmmirror.com/core-util-is/-/core-util-is-1.0.3.tgz", 495 | "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==" 496 | }, 497 | "node_modules/cors": { 498 | "version": "2.8.5", 499 | "resolved": "https://registry.npmmirror.com/cors/-/cors-2.8.5.tgz", 500 | "integrity": "sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==", 501 | "dependencies": { 502 | "object-assign": "^4", 503 | "vary": "^1" 504 | }, 505 | "engines": { 506 | "node": ">= 0.10" 507 | } 508 | }, 509 | "node_modules/create-require": { 510 | "version": "1.1.1", 511 | "resolved": "https://registry.npmmirror.com/create-require/-/create-require-1.1.1.tgz", 512 | "integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==", 513 | "dev": true 514 | }, 515 | "node_modules/debug": { 516 | "version": "2.6.9", 517 | "resolved": "https://registry.npmmirror.com/debug/-/debug-2.6.9.tgz", 518 | "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", 519 | "dependencies": { 520 | "ms": "2.0.0" 521 | } 522 | }, 523 | "node_modules/define-data-property": { 524 | "version": "1.1.4", 525 | "resolved": "https://registry.npmmirror.com/define-data-property/-/define-data-property-1.1.4.tgz", 526 | "integrity": "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==", 527 | "dependencies": { 528 | "es-define-property": "^1.0.0", 529 | "es-errors": "^1.3.0", 530 | "gopd": "^1.0.1" 531 | }, 532 | "engines": { 533 | "node": ">= 0.4" 534 | }, 535 | "funding": { 536 | "url": "https://github.com/sponsors/ljharb" 537 | } 538 | }, 539 | "node_modules/denque": { 540 | "version": "1.5.1", 541 | "resolved": "https://registry.npmmirror.com/denque/-/denque-1.5.1.tgz", 542 | "integrity": "sha512-XwE+iZ4D6ZUB7mfYRMb5wByE8L74HCn30FBN7sWnXksWc1LO1bPDl67pBR9o/kC4z/xSNAwkMYcGgqDV3BE3Hw==", 543 | "engines": { 544 | "node": ">=0.10" 545 | } 546 | }, 547 | "node_modules/depd": { 548 | "version": "2.0.0", 549 | "resolved": "https://registry.npmmirror.com/depd/-/depd-2.0.0.tgz", 550 | "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", 551 | "engines": { 552 | "node": ">= 0.8" 553 | } 554 | }, 555 | "node_modules/destroy": { 556 | "version": "1.2.0", 557 | "resolved": "https://registry.npmmirror.com/destroy/-/destroy-1.2.0.tgz", 558 | "integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==", 559 | "engines": { 560 | "node": ">= 0.8", 561 | "npm": "1.2.8000 || >= 1.4.16" 562 | } 563 | }, 564 | "node_modules/diff": { 565 | "version": "4.0.2", 566 | "resolved": "https://registry.npmmirror.com/diff/-/diff-4.0.2.tgz", 567 | "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==", 568 | "dev": true, 569 | "engines": { 570 | "node": ">=0.3.1" 571 | } 572 | }, 573 | "node_modules/dynamic-dedupe": { 574 | "version": "0.3.0", 575 | "resolved": "https://registry.npmmirror.com/dynamic-dedupe/-/dynamic-dedupe-0.3.0.tgz", 576 | "integrity": "sha512-ssuANeD+z97meYOqd50e04Ze5qp4bPqo8cCkI4TRjZkzAUgIDTrXV1R8QCdINpiI+hw14+rYazvTRdQrz0/rFQ==", 577 | "dev": true, 578 | "dependencies": { 579 | "xtend": "^4.0.0" 580 | } 581 | }, 582 | "node_modules/ecdsa-sig-formatter": { 583 | "version": "1.0.11", 584 | "resolved": "https://registry.npmmirror.com/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz", 585 | "integrity": "sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==", 586 | "dependencies": { 587 | "safe-buffer": "^5.0.1" 588 | } 589 | }, 590 | "node_modules/ee-first": { 591 | "version": "1.1.1", 592 | "resolved": "https://registry.npmmirror.com/ee-first/-/ee-first-1.1.1.tgz", 593 | "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==" 594 | }, 595 | "node_modules/encodeurl": { 596 | "version": "1.0.2", 597 | "resolved": "https://registry.npmmirror.com/encodeurl/-/encodeurl-1.0.2.tgz", 598 | "integrity": "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==", 599 | "engines": { 600 | "node": ">= 0.8" 601 | } 602 | }, 603 | "node_modules/es-define-property": { 604 | "version": "1.0.0", 605 | "resolved": "https://registry.npmmirror.com/es-define-property/-/es-define-property-1.0.0.tgz", 606 | "integrity": "sha512-jxayLKShrEqqzJ0eumQbVhTYQM27CfT1T35+gCgDFoL82JLsXqTJ76zv6A0YLOgEnLUMvLzsDsGIrl8NFpT2gQ==", 607 | "dependencies": { 608 | "get-intrinsic": "^1.2.4" 609 | }, 610 | "engines": { 611 | "node": ">= 0.4" 612 | } 613 | }, 614 | "node_modules/es-errors": { 615 | "version": "1.3.0", 616 | "resolved": "https://registry.npmmirror.com/es-errors/-/es-errors-1.3.0.tgz", 617 | "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", 618 | "engines": { 619 | "node": ">= 0.4" 620 | } 621 | }, 622 | "node_modules/escape-html": { 623 | "version": "1.0.3", 624 | "resolved": "https://registry.npmmirror.com/escape-html/-/escape-html-1.0.3.tgz", 625 | "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==" 626 | }, 627 | "node_modules/etag": { 628 | "version": "1.8.1", 629 | "resolved": "https://registry.npmmirror.com/etag/-/etag-1.8.1.tgz", 630 | "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==", 631 | "engines": { 632 | "node": ">= 0.6" 633 | } 634 | }, 635 | "node_modules/express": { 636 | "version": "4.19.2", 637 | "resolved": "https://registry.npmmirror.com/express/-/express-4.19.2.tgz", 638 | "integrity": "sha512-5T6nhjsT+EOMzuck8JjBHARTHfMht0POzlA60WV2pMD3gyXw2LZnZ+ueGdNxG+0calOJcWKbpFcuzLZ91YWq9Q==", 639 | "dependencies": { 640 | "accepts": "~1.3.8", 641 | "array-flatten": "1.1.1", 642 | "body-parser": "1.20.2", 643 | "content-disposition": "0.5.4", 644 | "content-type": "~1.0.4", 645 | "cookie": "0.6.0", 646 | "cookie-signature": "1.0.6", 647 | "debug": "2.6.9", 648 | "depd": "2.0.0", 649 | "encodeurl": "~1.0.2", 650 | "escape-html": "~1.0.3", 651 | "etag": "~1.8.1", 652 | "finalhandler": "1.2.0", 653 | "fresh": "0.5.2", 654 | "http-errors": "2.0.0", 655 | "merge-descriptors": "1.0.1", 656 | "methods": "~1.1.2", 657 | "on-finished": "2.4.1", 658 | "parseurl": "~1.3.3", 659 | "path-to-regexp": "0.1.7", 660 | "proxy-addr": "~2.0.7", 661 | "qs": "6.11.0", 662 | "range-parser": "~1.2.1", 663 | "safe-buffer": "5.2.1", 664 | "send": "0.18.0", 665 | "serve-static": "1.15.0", 666 | "setprototypeof": "1.2.0", 667 | "statuses": "2.0.1", 668 | "type-is": "~1.6.18", 669 | "utils-merge": "1.0.1", 670 | "vary": "~1.1.2" 671 | }, 672 | "engines": { 673 | "node": ">= 0.10.0" 674 | } 675 | }, 676 | "node_modules/fill-range": { 677 | "version": "7.1.1", 678 | "resolved": "https://registry.npmmirror.com/fill-range/-/fill-range-7.1.1.tgz", 679 | "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", 680 | "dev": true, 681 | "dependencies": { 682 | "to-regex-range": "^5.0.1" 683 | }, 684 | "engines": { 685 | "node": ">=8" 686 | } 687 | }, 688 | "node_modules/finalhandler": { 689 | "version": "1.2.0", 690 | "resolved": "https://registry.npmmirror.com/finalhandler/-/finalhandler-1.2.0.tgz", 691 | "integrity": "sha512-5uXcUVftlQMFnWC9qu/svkWv3GTd2PfUhK/3PLkYNAe7FbqJMt3515HaxE6eRL74GdsriiwujiawdaB1BpEISg==", 692 | "dependencies": { 693 | "debug": "2.6.9", 694 | "encodeurl": "~1.0.2", 695 | "escape-html": "~1.0.3", 696 | "on-finished": "2.4.1", 697 | "parseurl": "~1.3.3", 698 | "statuses": "2.0.1", 699 | "unpipe": "~1.0.0" 700 | }, 701 | "engines": { 702 | "node": ">= 0.8" 703 | } 704 | }, 705 | "node_modules/forwarded": { 706 | "version": "0.2.0", 707 | "resolved": "https://registry.npmmirror.com/forwarded/-/forwarded-0.2.0.tgz", 708 | "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==", 709 | "engines": { 710 | "node": ">= 0.6" 711 | } 712 | }, 713 | "node_modules/fresh": { 714 | "version": "0.5.2", 715 | "resolved": "https://registry.npmmirror.com/fresh/-/fresh-0.5.2.tgz", 716 | "integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==", 717 | "engines": { 718 | "node": ">= 0.6" 719 | } 720 | }, 721 | "node_modules/fs.realpath": { 722 | "version": "1.0.0", 723 | "resolved": "https://registry.npmmirror.com/fs.realpath/-/fs.realpath-1.0.0.tgz", 724 | "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", 725 | "dev": true 726 | }, 727 | "node_modules/fsevents": { 728 | "version": "2.3.3", 729 | "resolved": "https://registry.npmmirror.com/fsevents/-/fsevents-2.3.3.tgz", 730 | "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", 731 | "dev": true, 732 | "hasInstallScript": true, 733 | "optional": true, 734 | "os": [ 735 | "darwin" 736 | ], 737 | "engines": { 738 | "node": "^8.16.0 || ^10.6.0 || >=11.0.0" 739 | } 740 | }, 741 | "node_modules/function-bind": { 742 | "version": "1.1.2", 743 | "resolved": "https://registry.npmmirror.com/function-bind/-/function-bind-1.1.2.tgz", 744 | "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", 745 | "funding": { 746 | "url": "https://github.com/sponsors/ljharb" 747 | } 748 | }, 749 | "node_modules/get-intrinsic": { 750 | "version": "1.2.4", 751 | "resolved": "https://registry.npmmirror.com/get-intrinsic/-/get-intrinsic-1.2.4.tgz", 752 | "integrity": "sha512-5uYhsJH8VJBTv7oslg4BznJYhDoRI6waYCxMmCdnTrcCrHA/fCFKoTFz2JKKE0HdDFUF7/oQuhzumXJK7paBRQ==", 753 | "dependencies": { 754 | "es-errors": "^1.3.0", 755 | "function-bind": "^1.1.2", 756 | "has-proto": "^1.0.1", 757 | "has-symbols": "^1.0.3", 758 | "hasown": "^2.0.0" 759 | }, 760 | "engines": { 761 | "node": ">= 0.4" 762 | }, 763 | "funding": { 764 | "url": "https://github.com/sponsors/ljharb" 765 | } 766 | }, 767 | "node_modules/glob": { 768 | "version": "7.2.3", 769 | "resolved": "https://registry.npmmirror.com/glob/-/glob-7.2.3.tgz", 770 | "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", 771 | "deprecated": "Glob versions prior to v9 are no longer supported", 772 | "dev": true, 773 | "dependencies": { 774 | "fs.realpath": "^1.0.0", 775 | "inflight": "^1.0.4", 776 | "inherits": "2", 777 | "minimatch": "^3.1.1", 778 | "once": "^1.3.0", 779 | "path-is-absolute": "^1.0.0" 780 | }, 781 | "engines": { 782 | "node": "*" 783 | }, 784 | "funding": { 785 | "url": "https://github.com/sponsors/isaacs" 786 | } 787 | }, 788 | "node_modules/glob-parent": { 789 | "version": "5.1.2", 790 | "resolved": "https://registry.npmmirror.com/glob-parent/-/glob-parent-5.1.2.tgz", 791 | "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", 792 | "dev": true, 793 | "dependencies": { 794 | "is-glob": "^4.0.1" 795 | }, 796 | "engines": { 797 | "node": ">= 6" 798 | } 799 | }, 800 | "node_modules/gopd": { 801 | "version": "1.0.1", 802 | "resolved": "https://registry.npmmirror.com/gopd/-/gopd-1.0.1.tgz", 803 | "integrity": "sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==", 804 | "dependencies": { 805 | "get-intrinsic": "^1.1.3" 806 | }, 807 | "funding": { 808 | "url": "https://github.com/sponsors/ljharb" 809 | } 810 | }, 811 | "node_modules/has-property-descriptors": { 812 | "version": "1.0.2", 813 | "resolved": "https://registry.npmmirror.com/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz", 814 | "integrity": "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==", 815 | "dependencies": { 816 | "es-define-property": "^1.0.0" 817 | }, 818 | "funding": { 819 | "url": "https://github.com/sponsors/ljharb" 820 | } 821 | }, 822 | "node_modules/has-proto": { 823 | "version": "1.0.3", 824 | "resolved": "https://registry.npmmirror.com/has-proto/-/has-proto-1.0.3.tgz", 825 | "integrity": "sha512-SJ1amZAJUiZS+PhsVLf5tGydlaVB8EdFpaSO4gmiUKUOxk8qzn5AIy4ZeJUmh22znIdk/uMAUT2pl3FxzVUH+Q==", 826 | "engines": { 827 | "node": ">= 0.4" 828 | }, 829 | "funding": { 830 | "url": "https://github.com/sponsors/ljharb" 831 | } 832 | }, 833 | "node_modules/has-symbols": { 834 | "version": "1.0.3", 835 | "resolved": "https://registry.npmmirror.com/has-symbols/-/has-symbols-1.0.3.tgz", 836 | "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==", 837 | "engines": { 838 | "node": ">= 0.4" 839 | }, 840 | "funding": { 841 | "url": "https://github.com/sponsors/ljharb" 842 | } 843 | }, 844 | "node_modules/hasown": { 845 | "version": "2.0.2", 846 | "resolved": "https://registry.npmmirror.com/hasown/-/hasown-2.0.2.tgz", 847 | "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", 848 | "dependencies": { 849 | "function-bind": "^1.1.2" 850 | }, 851 | "engines": { 852 | "node": ">= 0.4" 853 | } 854 | }, 855 | "node_modules/http-errors": { 856 | "version": "2.0.0", 857 | "resolved": "https://registry.npmmirror.com/http-errors/-/http-errors-2.0.0.tgz", 858 | "integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==", 859 | "dependencies": { 860 | "depd": "2.0.0", 861 | "inherits": "2.0.4", 862 | "setprototypeof": "1.2.0", 863 | "statuses": "2.0.1", 864 | "toidentifier": "1.0.1" 865 | }, 866 | "engines": { 867 | "node": ">= 0.8" 868 | } 869 | }, 870 | "node_modules/iconv-lite": { 871 | "version": "0.4.24", 872 | "resolved": "https://registry.npmmirror.com/iconv-lite/-/iconv-lite-0.4.24.tgz", 873 | "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", 874 | "dependencies": { 875 | "safer-buffer": ">= 2.1.2 < 3" 876 | }, 877 | "engines": { 878 | "node": ">=0.10.0" 879 | } 880 | }, 881 | "node_modules/inflight": { 882 | "version": "1.0.6", 883 | "resolved": "https://registry.npmmirror.com/inflight/-/inflight-1.0.6.tgz", 884 | "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", 885 | "deprecated": "This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful.", 886 | "dev": true, 887 | "dependencies": { 888 | "once": "^1.3.0", 889 | "wrappy": "1" 890 | } 891 | }, 892 | "node_modules/inherits": { 893 | "version": "2.0.4", 894 | "resolved": "https://registry.npmmirror.com/inherits/-/inherits-2.0.4.tgz", 895 | "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" 896 | }, 897 | "node_modules/ipaddr.js": { 898 | "version": "1.9.1", 899 | "resolved": "https://registry.npmmirror.com/ipaddr.js/-/ipaddr.js-1.9.1.tgz", 900 | "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==", 901 | "engines": { 902 | "node": ">= 0.10" 903 | } 904 | }, 905 | "node_modules/is-binary-path": { 906 | "version": "2.1.0", 907 | "resolved": "https://registry.npmmirror.com/is-binary-path/-/is-binary-path-2.1.0.tgz", 908 | "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", 909 | "dev": true, 910 | "dependencies": { 911 | "binary-extensions": "^2.0.0" 912 | }, 913 | "engines": { 914 | "node": ">=8" 915 | } 916 | }, 917 | "node_modules/is-core-module": { 918 | "version": "2.13.1", 919 | "resolved": "https://registry.npmmirror.com/is-core-module/-/is-core-module-2.13.1.tgz", 920 | "integrity": "sha512-hHrIjvZsftOsvKSn2TRYl63zvxsgE0K+0mYMoH6gD4omR5IWB2KynivBQczo3+wF1cCkjzvptnI9Q0sPU66ilw==", 921 | "dev": true, 922 | "dependencies": { 923 | "hasown": "^2.0.0" 924 | }, 925 | "funding": { 926 | "url": "https://github.com/sponsors/ljharb" 927 | } 928 | }, 929 | "node_modules/is-extglob": { 930 | "version": "2.1.1", 931 | "resolved": "https://registry.npmmirror.com/is-extglob/-/is-extglob-2.1.1.tgz", 932 | "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", 933 | "dev": true, 934 | "engines": { 935 | "node": ">=0.10.0" 936 | } 937 | }, 938 | "node_modules/is-glob": { 939 | "version": "4.0.3", 940 | "resolved": "https://registry.npmmirror.com/is-glob/-/is-glob-4.0.3.tgz", 941 | "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", 942 | "dev": true, 943 | "dependencies": { 944 | "is-extglob": "^2.1.1" 945 | }, 946 | "engines": { 947 | "node": ">=0.10.0" 948 | } 949 | }, 950 | "node_modules/is-number": { 951 | "version": "7.0.0", 952 | "resolved": "https://registry.npmmirror.com/is-number/-/is-number-7.0.0.tgz", 953 | "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", 954 | "dev": true, 955 | "engines": { 956 | "node": ">=0.12.0" 957 | } 958 | }, 959 | "node_modules/isarray": { 960 | "version": "1.0.0", 961 | "resolved": "https://registry.npmmirror.com/isarray/-/isarray-1.0.0.tgz", 962 | "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==" 963 | }, 964 | "node_modules/jsonwebtoken": { 965 | "version": "8.5.1", 966 | "resolved": "https://registry.npmmirror.com/jsonwebtoken/-/jsonwebtoken-8.5.1.tgz", 967 | "integrity": "sha512-XjwVfRS6jTMsqYs0EsuJ4LGxXV14zQybNd4L2r0UvbVnSF9Af8x7p5MzbJ90Ioz/9TI41/hTCvznF/loiSzn8w==", 968 | "dependencies": { 969 | "jws": "^3.2.2", 970 | "lodash.includes": "^4.3.0", 971 | "lodash.isboolean": "^3.0.3", 972 | "lodash.isinteger": "^4.0.4", 973 | "lodash.isnumber": "^3.0.3", 974 | "lodash.isplainobject": "^4.0.6", 975 | "lodash.isstring": "^4.0.1", 976 | "lodash.once": "^4.0.0", 977 | "ms": "^2.1.1", 978 | "semver": "^5.6.0" 979 | }, 980 | "engines": { 981 | "node": ">=4", 982 | "npm": ">=1.4.28" 983 | } 984 | }, 985 | "node_modules/jsonwebtoken/node_modules/ms": { 986 | "version": "2.1.3", 987 | "resolved": "https://registry.npmmirror.com/ms/-/ms-2.1.3.tgz", 988 | "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" 989 | }, 990 | "node_modules/jwa": { 991 | "version": "1.4.1", 992 | "resolved": "https://registry.npmmirror.com/jwa/-/jwa-1.4.1.tgz", 993 | "integrity": "sha512-qiLX/xhEEFKUAJ6FiBMbes3w9ATzyk5W7Hvzpa/SLYdxNtng+gcurvrI7TbACjIXlsJyr05/S1oUhZrc63evQA==", 994 | "dependencies": { 995 | "buffer-equal-constant-time": "1.0.1", 996 | "ecdsa-sig-formatter": "1.0.11", 997 | "safe-buffer": "^5.0.1" 998 | } 999 | }, 1000 | "node_modules/jws": { 1001 | "version": "3.2.2", 1002 | "resolved": "https://registry.npmmirror.com/jws/-/jws-3.2.2.tgz", 1003 | "integrity": "sha512-YHlZCB6lMTllWDtSPHz/ZXTsi8S00usEV6v1tjq8tOUZzw7DpSDWVXjXDre6ed1w/pd495ODpHZYSdkRTsa0HA==", 1004 | "dependencies": { 1005 | "jwa": "^1.4.1", 1006 | "safe-buffer": "^5.0.1" 1007 | } 1008 | }, 1009 | "node_modules/kareem": { 1010 | "version": "2.3.2", 1011 | "resolved": "https://registry.npmmirror.com/kareem/-/kareem-2.3.2.tgz", 1012 | "integrity": "sha512-STHz9P7X2L4Kwn72fA4rGyqyXdmrMSdxqHx9IXon/FXluXieaFA6KJ2upcHAHxQPQ0LeM/OjLrhFxifHewOALQ==" 1013 | }, 1014 | "node_modules/lodash.includes": { 1015 | "version": "4.3.0", 1016 | "resolved": "https://registry.npmmirror.com/lodash.includes/-/lodash.includes-4.3.0.tgz", 1017 | "integrity": "sha512-W3Bx6mdkRTGtlJISOvVD/lbqjTlPPUDTMnlXZFnVwi9NKJ6tiAk6LVdlhZMm17VZisqhKcgzpO5Wz91PCt5b0w==" 1018 | }, 1019 | "node_modules/lodash.isboolean": { 1020 | "version": "3.0.3", 1021 | "resolved": "https://registry.npmmirror.com/lodash.isboolean/-/lodash.isboolean-3.0.3.tgz", 1022 | "integrity": "sha512-Bz5mupy2SVbPHURB98VAcw+aHh4vRV5IPNhILUCsOzRmsTmSQ17jIuqopAentWoehktxGd9e/hbIXq980/1QJg==" 1023 | }, 1024 | "node_modules/lodash.isinteger": { 1025 | "version": "4.0.4", 1026 | "resolved": "https://registry.npmmirror.com/lodash.isinteger/-/lodash.isinteger-4.0.4.tgz", 1027 | "integrity": "sha512-DBwtEWN2caHQ9/imiNeEA5ys1JoRtRfY3d7V9wkqtbycnAmTvRRmbHKDV4a0EYc678/dia0jrte4tjYwVBaZUA==" 1028 | }, 1029 | "node_modules/lodash.isnumber": { 1030 | "version": "3.0.3", 1031 | "resolved": "https://registry.npmmirror.com/lodash.isnumber/-/lodash.isnumber-3.0.3.tgz", 1032 | "integrity": "sha512-QYqzpfwO3/CWf3XP+Z+tkQsfaLL/EnUlXWVkIk5FUPc4sBdTehEqZONuyRt2P67PXAk+NXmTBcc97zw9t1FQrw==" 1033 | }, 1034 | "node_modules/lodash.isplainobject": { 1035 | "version": "4.0.6", 1036 | "resolved": "https://registry.npmmirror.com/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz", 1037 | "integrity": "sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA==" 1038 | }, 1039 | "node_modules/lodash.isstring": { 1040 | "version": "4.0.1", 1041 | "resolved": "https://registry.npmmirror.com/lodash.isstring/-/lodash.isstring-4.0.1.tgz", 1042 | "integrity": "sha512-0wJxfxH1wgO3GrbuP+dTTk7op+6L41QCXbGINEmD+ny/G/eCqGzxyCsh7159S+mgDDcoarnBw6PC1PS5+wUGgw==" 1043 | }, 1044 | "node_modules/lodash.once": { 1045 | "version": "4.1.1", 1046 | "resolved": "https://registry.npmmirror.com/lodash.once/-/lodash.once-4.1.1.tgz", 1047 | "integrity": "sha512-Sb487aTOCr9drQVL8pIxOzVhafOjZN9UU54hiN8PU3uAiSV7lx1yYNpbNmex2PK6dSJoNTSJUUswT651yww3Mg==" 1048 | }, 1049 | "node_modules/make-error": { 1050 | "version": "1.3.6", 1051 | "resolved": "https://registry.npmmirror.com/make-error/-/make-error-1.3.6.tgz", 1052 | "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==", 1053 | "dev": true 1054 | }, 1055 | "node_modules/media-typer": { 1056 | "version": "0.3.0", 1057 | "resolved": "https://registry.npmmirror.com/media-typer/-/media-typer-0.3.0.tgz", 1058 | "integrity": "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==", 1059 | "engines": { 1060 | "node": ">= 0.6" 1061 | } 1062 | }, 1063 | "node_modules/memory-pager": { 1064 | "version": "1.5.0", 1065 | "resolved": "https://registry.npmmirror.com/memory-pager/-/memory-pager-1.5.0.tgz", 1066 | "integrity": "sha512-ZS4Bp4r/Zoeq6+NLJpP+0Zzm0pR8whtGPf1XExKLJBAczGMnSi3It14OiNCStjQjM6NU1okjQGSxgEZN8eBYKg==", 1067 | "optional": true 1068 | }, 1069 | "node_modules/merge-descriptors": { 1070 | "version": "1.0.1", 1071 | "resolved": "https://registry.npmmirror.com/merge-descriptors/-/merge-descriptors-1.0.1.tgz", 1072 | "integrity": "sha512-cCi6g3/Zr1iqQi6ySbseM1Xvooa98N0w31jzUYrXPX2xqObmFGHJ0tQ5u74H3mVh7wLouTseZyYIq39g8cNp1w==" 1073 | }, 1074 | "node_modules/methods": { 1075 | "version": "1.1.2", 1076 | "resolved": "https://registry.npmmirror.com/methods/-/methods-1.1.2.tgz", 1077 | "integrity": "sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==", 1078 | "engines": { 1079 | "node": ">= 0.6" 1080 | } 1081 | }, 1082 | "node_modules/mime": { 1083 | "version": "1.6.0", 1084 | "resolved": "https://registry.npmmirror.com/mime/-/mime-1.6.0.tgz", 1085 | "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", 1086 | "bin": { 1087 | "mime": "cli.js" 1088 | }, 1089 | "engines": { 1090 | "node": ">=4" 1091 | } 1092 | }, 1093 | "node_modules/mime-db": { 1094 | "version": "1.52.0", 1095 | "resolved": "https://registry.npmmirror.com/mime-db/-/mime-db-1.52.0.tgz", 1096 | "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", 1097 | "engines": { 1098 | "node": ">= 0.6" 1099 | } 1100 | }, 1101 | "node_modules/mime-types": { 1102 | "version": "2.1.35", 1103 | "resolved": "https://registry.npmmirror.com/mime-types/-/mime-types-2.1.35.tgz", 1104 | "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", 1105 | "dependencies": { 1106 | "mime-db": "1.52.0" 1107 | }, 1108 | "engines": { 1109 | "node": ">= 0.6" 1110 | } 1111 | }, 1112 | "node_modules/minimatch": { 1113 | "version": "3.1.2", 1114 | "resolved": "https://registry.npmmirror.com/minimatch/-/minimatch-3.1.2.tgz", 1115 | "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", 1116 | "dev": true, 1117 | "dependencies": { 1118 | "brace-expansion": "^1.1.7" 1119 | }, 1120 | "engines": { 1121 | "node": "*" 1122 | } 1123 | }, 1124 | "node_modules/minimist": { 1125 | "version": "1.2.8", 1126 | "resolved": "https://registry.npmmirror.com/minimist/-/minimist-1.2.8.tgz", 1127 | "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", 1128 | "dev": true, 1129 | "funding": { 1130 | "url": "https://github.com/sponsors/ljharb" 1131 | } 1132 | }, 1133 | "node_modules/mkdirp": { 1134 | "version": "1.0.4", 1135 | "resolved": "https://registry.npmmirror.com/mkdirp/-/mkdirp-1.0.4.tgz", 1136 | "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", 1137 | "dev": true, 1138 | "bin": { 1139 | "mkdirp": "bin/cmd.js" 1140 | }, 1141 | "engines": { 1142 | "node": ">=10" 1143 | } 1144 | }, 1145 | "node_modules/mongodb": { 1146 | "version": "3.7.4", 1147 | "resolved": "https://registry.npmmirror.com/mongodb/-/mongodb-3.7.4.tgz", 1148 | "integrity": "sha512-K5q8aBqEXMwWdVNh94UQTwZ6BejVbFhh1uB6c5FKtPE9eUMZPUO3sRZdgIEcHSrAWmxzpG/FeODDKL388sqRmw==", 1149 | "dependencies": { 1150 | "bl": "^2.2.1", 1151 | "bson": "^1.1.4", 1152 | "denque": "^1.4.1", 1153 | "optional-require": "^1.1.8", 1154 | "safe-buffer": "^5.1.2" 1155 | }, 1156 | "engines": { 1157 | "node": ">=4" 1158 | }, 1159 | "optionalDependencies": { 1160 | "saslprep": "^1.0.0" 1161 | }, 1162 | "peerDependenciesMeta": { 1163 | "aws4": { 1164 | "optional": true 1165 | }, 1166 | "bson-ext": { 1167 | "optional": true 1168 | }, 1169 | "kerberos": { 1170 | "optional": true 1171 | }, 1172 | "mongodb-client-encryption": { 1173 | "optional": true 1174 | }, 1175 | "mongodb-extjson": { 1176 | "optional": true 1177 | }, 1178 | "snappy": { 1179 | "optional": true 1180 | } 1181 | } 1182 | }, 1183 | "node_modules/mongodb/node_modules/optional-require": { 1184 | "version": "1.1.8", 1185 | "resolved": "https://registry.npmmirror.com/optional-require/-/optional-require-1.1.8.tgz", 1186 | "integrity": "sha512-jq83qaUb0wNg9Krv1c5OQ+58EK+vHde6aBPzLvPPqJm89UQWsvSuFy9X/OSNJnFeSOKo7btE0n8Nl2+nE+z5nA==", 1187 | "dependencies": { 1188 | "require-at": "^1.0.6" 1189 | }, 1190 | "engines": { 1191 | "node": ">=4" 1192 | } 1193 | }, 1194 | "node_modules/mongoose": { 1195 | "version": "5.13.22", 1196 | "resolved": "https://registry.npmmirror.com/mongoose/-/mongoose-5.13.22.tgz", 1197 | "integrity": "sha512-p51k/c4X/MfqeQ3I1ranlDiggLzNumZrTDD9CeezHwZxt2/btf+YZD7MCe07RAY2NgFYVMayq6jMamw02Jmf9w==", 1198 | "dependencies": { 1199 | "@types/bson": "1.x || 4.0.x", 1200 | "@types/mongodb": "^3.5.27", 1201 | "bson": "^1.1.4", 1202 | "kareem": "2.3.2", 1203 | "mongodb": "3.7.4", 1204 | "mongoose-legacy-pluralize": "1.0.2", 1205 | "mpath": "0.8.4", 1206 | "mquery": "3.2.5", 1207 | "ms": "2.1.2", 1208 | "optional-require": "1.0.x", 1209 | "regexp-clone": "1.0.0", 1210 | "safe-buffer": "5.2.1", 1211 | "sift": "13.5.2", 1212 | "sliced": "1.0.1" 1213 | }, 1214 | "engines": { 1215 | "node": ">=4.0.0" 1216 | }, 1217 | "funding": { 1218 | "type": "opencollective", 1219 | "url": "https://opencollective.com/mongoose" 1220 | } 1221 | }, 1222 | "node_modules/mongoose-legacy-pluralize": { 1223 | "version": "1.0.2", 1224 | "resolved": "https://registry.npmmirror.com/mongoose-legacy-pluralize/-/mongoose-legacy-pluralize-1.0.2.tgz", 1225 | "integrity": "sha512-Yo/7qQU4/EyIS8YDFSeenIvXxZN+ld7YdV9LqFVQJzTLye8unujAWPZ4NWKfFA+RNjh+wvTWKY9Z3E5XM6ZZiQ==", 1226 | "peerDependencies": { 1227 | "mongoose": "*" 1228 | } 1229 | }, 1230 | "node_modules/mongoose/node_modules/ms": { 1231 | "version": "2.1.2", 1232 | "resolved": "https://registry.npmmirror.com/ms/-/ms-2.1.2.tgz", 1233 | "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" 1234 | }, 1235 | "node_modules/mpath": { 1236 | "version": "0.8.4", 1237 | "resolved": "https://registry.npmmirror.com/mpath/-/mpath-0.8.4.tgz", 1238 | "integrity": "sha512-DTxNZomBcTWlrMW76jy1wvV37X/cNNxPW1y2Jzd4DZkAaC5ZGsm8bfGfNOthcDuRJujXLqiuS6o3Tpy0JEoh7g==", 1239 | "engines": { 1240 | "node": ">=4.0.0" 1241 | } 1242 | }, 1243 | "node_modules/mquery": { 1244 | "version": "3.2.5", 1245 | "resolved": "https://registry.npmmirror.com/mquery/-/mquery-3.2.5.tgz", 1246 | "integrity": "sha512-VjOKHHgU84wij7IUoZzFRU07IAxd5kWJaDmyUzQlbjHjyoeK5TNeeo8ZsFDtTYnSgpW6n/nMNIHvE3u8Lbrf4A==", 1247 | "dependencies": { 1248 | "bluebird": "3.5.1", 1249 | "debug": "3.1.0", 1250 | "regexp-clone": "^1.0.0", 1251 | "safe-buffer": "5.1.2", 1252 | "sliced": "1.0.1" 1253 | }, 1254 | "engines": { 1255 | "node": ">=4.0.0" 1256 | } 1257 | }, 1258 | "node_modules/mquery/node_modules/debug": { 1259 | "version": "3.1.0", 1260 | "resolved": "https://registry.npmmirror.com/debug/-/debug-3.1.0.tgz", 1261 | "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", 1262 | "dependencies": { 1263 | "ms": "2.0.0" 1264 | } 1265 | }, 1266 | "node_modules/mquery/node_modules/safe-buffer": { 1267 | "version": "5.1.2", 1268 | "resolved": "https://registry.npmmirror.com/safe-buffer/-/safe-buffer-5.1.2.tgz", 1269 | "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" 1270 | }, 1271 | "node_modules/ms": { 1272 | "version": "2.0.0", 1273 | "resolved": "https://registry.npmmirror.com/ms/-/ms-2.0.0.tgz", 1274 | "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" 1275 | }, 1276 | "node_modules/negotiator": { 1277 | "version": "0.6.3", 1278 | "resolved": "https://registry.npmmirror.com/negotiator/-/negotiator-0.6.3.tgz", 1279 | "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==", 1280 | "engines": { 1281 | "node": ">= 0.6" 1282 | } 1283 | }, 1284 | "node_modules/normalize-path": { 1285 | "version": "3.0.0", 1286 | "resolved": "https://registry.npmmirror.com/normalize-path/-/normalize-path-3.0.0.tgz", 1287 | "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", 1288 | "dev": true, 1289 | "engines": { 1290 | "node": ">=0.10.0" 1291 | } 1292 | }, 1293 | "node_modules/object-assign": { 1294 | "version": "4.1.1", 1295 | "resolved": "https://registry.npmmirror.com/object-assign/-/object-assign-4.1.1.tgz", 1296 | "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", 1297 | "engines": { 1298 | "node": ">=0.10.0" 1299 | } 1300 | }, 1301 | "node_modules/object-inspect": { 1302 | "version": "1.13.1", 1303 | "resolved": "https://registry.npmmirror.com/object-inspect/-/object-inspect-1.13.1.tgz", 1304 | "integrity": "sha512-5qoj1RUiKOMsCCNLV1CBiPYE10sziTsnmNxkAI/rZhiD63CF7IqdFGC/XzjWjpSgLf0LxXX3bDFIh0E18f6UhQ==", 1305 | "funding": { 1306 | "url": "https://github.com/sponsors/ljharb" 1307 | } 1308 | }, 1309 | "node_modules/on-finished": { 1310 | "version": "2.4.1", 1311 | "resolved": "https://registry.npmmirror.com/on-finished/-/on-finished-2.4.1.tgz", 1312 | "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==", 1313 | "dependencies": { 1314 | "ee-first": "1.1.1" 1315 | }, 1316 | "engines": { 1317 | "node": ">= 0.8" 1318 | } 1319 | }, 1320 | "node_modules/once": { 1321 | "version": "1.4.0", 1322 | "resolved": "https://registry.npmmirror.com/once/-/once-1.4.0.tgz", 1323 | "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", 1324 | "dev": true, 1325 | "dependencies": { 1326 | "wrappy": "1" 1327 | } 1328 | }, 1329 | "node_modules/optional-require": { 1330 | "version": "1.0.3", 1331 | "resolved": "https://registry.npmmirror.com/optional-require/-/optional-require-1.0.3.tgz", 1332 | "integrity": "sha512-RV2Zp2MY2aeYK5G+B/Sps8lW5NHAzE5QClbFP15j+PWmP+T9PxlJXBOOLoSAdgwFvS4t0aMR4vpedMkbHfh0nA==", 1333 | "engines": { 1334 | "node": ">=4" 1335 | } 1336 | }, 1337 | "node_modules/parseurl": { 1338 | "version": "1.3.3", 1339 | "resolved": "https://registry.npmmirror.com/parseurl/-/parseurl-1.3.3.tgz", 1340 | "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==", 1341 | "engines": { 1342 | "node": ">= 0.8" 1343 | } 1344 | }, 1345 | "node_modules/path-is-absolute": { 1346 | "version": "1.0.1", 1347 | "resolved": "https://registry.npmmirror.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz", 1348 | "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", 1349 | "dev": true, 1350 | "engines": { 1351 | "node": ">=0.10.0" 1352 | } 1353 | }, 1354 | "node_modules/path-parse": { 1355 | "version": "1.0.7", 1356 | "resolved": "https://registry.npmmirror.com/path-parse/-/path-parse-1.0.7.tgz", 1357 | "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", 1358 | "dev": true 1359 | }, 1360 | "node_modules/path-to-regexp": { 1361 | "version": "0.1.7", 1362 | "resolved": "https://registry.npmmirror.com/path-to-regexp/-/path-to-regexp-0.1.7.tgz", 1363 | "integrity": "sha512-5DFkuoqlv1uYQKxy8omFBeJPQcdoE07Kv2sferDCrAq1ohOU+MSDswDIbnx3YAM60qIOnYa53wBhXW0EbMonrQ==" 1364 | }, 1365 | "node_modules/picomatch": { 1366 | "version": "2.3.1", 1367 | "resolved": "https://registry.npmmirror.com/picomatch/-/picomatch-2.3.1.tgz", 1368 | "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", 1369 | "dev": true, 1370 | "engines": { 1371 | "node": ">=8.6" 1372 | }, 1373 | "funding": { 1374 | "url": "https://github.com/sponsors/jonschlinkert" 1375 | } 1376 | }, 1377 | "node_modules/process-nextick-args": { 1378 | "version": "2.0.1", 1379 | "resolved": "https://registry.npmmirror.com/process-nextick-args/-/process-nextick-args-2.0.1.tgz", 1380 | "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==" 1381 | }, 1382 | "node_modules/proxy-addr": { 1383 | "version": "2.0.7", 1384 | "resolved": "https://registry.npmmirror.com/proxy-addr/-/proxy-addr-2.0.7.tgz", 1385 | "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==", 1386 | "dependencies": { 1387 | "forwarded": "0.2.0", 1388 | "ipaddr.js": "1.9.1" 1389 | }, 1390 | "engines": { 1391 | "node": ">= 0.10" 1392 | } 1393 | }, 1394 | "node_modules/qs": { 1395 | "version": "6.11.0", 1396 | "resolved": "https://registry.npmmirror.com/qs/-/qs-6.11.0.tgz", 1397 | "integrity": "sha512-MvjoMCJwEarSbUYk5O+nmoSzSutSsTwF85zcHPQ9OrlFoZOYIjaqBAJIqIXjptyD5vThxGq52Xu/MaJzRkIk4Q==", 1398 | "dependencies": { 1399 | "side-channel": "^1.0.4" 1400 | }, 1401 | "engines": { 1402 | "node": ">=0.6" 1403 | }, 1404 | "funding": { 1405 | "url": "https://github.com/sponsors/ljharb" 1406 | } 1407 | }, 1408 | "node_modules/range-parser": { 1409 | "version": "1.2.1", 1410 | "resolved": "https://registry.npmmirror.com/range-parser/-/range-parser-1.2.1.tgz", 1411 | "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==", 1412 | "engines": { 1413 | "node": ">= 0.6" 1414 | } 1415 | }, 1416 | "node_modules/raw-body": { 1417 | "version": "2.5.2", 1418 | "resolved": "https://registry.npmmirror.com/raw-body/-/raw-body-2.5.2.tgz", 1419 | "integrity": "sha512-8zGqypfENjCIqGhgXToC8aB2r7YrBX+AQAfIPs/Mlk+BtPTztOvTS01NRW/3Eh60J+a48lt8qsCzirQ6loCVfA==", 1420 | "dependencies": { 1421 | "bytes": "3.1.2", 1422 | "http-errors": "2.0.0", 1423 | "iconv-lite": "0.4.24", 1424 | "unpipe": "1.0.0" 1425 | }, 1426 | "engines": { 1427 | "node": ">= 0.8" 1428 | } 1429 | }, 1430 | "node_modules/readable-stream": { 1431 | "version": "2.3.8", 1432 | "resolved": "https://registry.npmmirror.com/readable-stream/-/readable-stream-2.3.8.tgz", 1433 | "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", 1434 | "dependencies": { 1435 | "core-util-is": "~1.0.0", 1436 | "inherits": "~2.0.3", 1437 | "isarray": "~1.0.0", 1438 | "process-nextick-args": "~2.0.0", 1439 | "safe-buffer": "~5.1.1", 1440 | "string_decoder": "~1.1.1", 1441 | "util-deprecate": "~1.0.1" 1442 | } 1443 | }, 1444 | "node_modules/readable-stream/node_modules/safe-buffer": { 1445 | "version": "5.1.2", 1446 | "resolved": "https://registry.npmmirror.com/safe-buffer/-/safe-buffer-5.1.2.tgz", 1447 | "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" 1448 | }, 1449 | "node_modules/readdirp": { 1450 | "version": "3.6.0", 1451 | "resolved": "https://registry.npmmirror.com/readdirp/-/readdirp-3.6.0.tgz", 1452 | "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", 1453 | "dev": true, 1454 | "dependencies": { 1455 | "picomatch": "^2.2.1" 1456 | }, 1457 | "engines": { 1458 | "node": ">=8.10.0" 1459 | } 1460 | }, 1461 | "node_modules/regexp-clone": { 1462 | "version": "1.0.0", 1463 | "resolved": "https://registry.npmmirror.com/regexp-clone/-/regexp-clone-1.0.0.tgz", 1464 | "integrity": "sha512-TuAasHQNamyyJ2hb97IuBEif4qBHGjPHBS64sZwytpLEqtBQ1gPJTnOaQ6qmpET16cK14kkjbazl6+p0RRv0yw==" 1465 | }, 1466 | "node_modules/require-at": { 1467 | "version": "1.0.6", 1468 | "resolved": "https://registry.npmmirror.com/require-at/-/require-at-1.0.6.tgz", 1469 | "integrity": "sha512-7i1auJbMUrXEAZCOQ0VNJgmcT2VOKPRl2YGJwgpHpC9CE91Mv4/4UYIUm4chGJaI381ZDq1JUicFii64Hapd8g==", 1470 | "engines": { 1471 | "node": ">=4" 1472 | } 1473 | }, 1474 | "node_modules/resolve": { 1475 | "version": "1.22.8", 1476 | "resolved": "https://registry.npmmirror.com/resolve/-/resolve-1.22.8.tgz", 1477 | "integrity": "sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw==", 1478 | "dev": true, 1479 | "dependencies": { 1480 | "is-core-module": "^2.13.0", 1481 | "path-parse": "^1.0.7", 1482 | "supports-preserve-symlinks-flag": "^1.0.0" 1483 | }, 1484 | "bin": { 1485 | "resolve": "bin/resolve" 1486 | }, 1487 | "funding": { 1488 | "url": "https://github.com/sponsors/ljharb" 1489 | } 1490 | }, 1491 | "node_modules/rimraf": { 1492 | "version": "2.7.1", 1493 | "resolved": "https://registry.npmmirror.com/rimraf/-/rimraf-2.7.1.tgz", 1494 | "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==", 1495 | "deprecated": "Rimraf versions prior to v4 are no longer supported", 1496 | "dev": true, 1497 | "dependencies": { 1498 | "glob": "^7.1.3" 1499 | }, 1500 | "bin": { 1501 | "rimraf": "bin.js" 1502 | } 1503 | }, 1504 | "node_modules/safe-buffer": { 1505 | "version": "5.2.1", 1506 | "resolved": "https://registry.npmmirror.com/safe-buffer/-/safe-buffer-5.2.1.tgz", 1507 | "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", 1508 | "funding": [ 1509 | { 1510 | "type": "github", 1511 | "url": "https://github.com/sponsors/feross" 1512 | }, 1513 | { 1514 | "type": "patreon", 1515 | "url": "https://www.patreon.com/feross" 1516 | }, 1517 | { 1518 | "type": "consulting", 1519 | "url": "https://feross.org/support" 1520 | } 1521 | ] 1522 | }, 1523 | "node_modules/safer-buffer": { 1524 | "version": "2.1.2", 1525 | "resolved": "https://registry.npmmirror.com/safer-buffer/-/safer-buffer-2.1.2.tgz", 1526 | "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" 1527 | }, 1528 | "node_modules/saslprep": { 1529 | "version": "1.0.3", 1530 | "resolved": "https://registry.npmmirror.com/saslprep/-/saslprep-1.0.3.tgz", 1531 | "integrity": "sha512-/MY/PEMbk2SuY5sScONwhUDsV2p77Znkb/q3nSVstq/yQzYJOH/Azh29p9oJLsl3LnQwSvZDKagDGBsBwSooag==", 1532 | "optional": true, 1533 | "dependencies": { 1534 | "sparse-bitfield": "^3.0.3" 1535 | }, 1536 | "engines": { 1537 | "node": ">=6" 1538 | } 1539 | }, 1540 | "node_modules/semver": { 1541 | "version": "5.7.2", 1542 | "resolved": "https://registry.npmmirror.com/semver/-/semver-5.7.2.tgz", 1543 | "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==", 1544 | "bin": { 1545 | "semver": "bin/semver" 1546 | } 1547 | }, 1548 | "node_modules/send": { 1549 | "version": "0.18.0", 1550 | "resolved": "https://registry.npmmirror.com/send/-/send-0.18.0.tgz", 1551 | "integrity": "sha512-qqWzuOjSFOuqPjFe4NOsMLafToQQwBSOEpS+FwEt3A2V3vKubTquT3vmLTQpFgMXp8AlFWFuP1qKaJZOtPpVXg==", 1552 | "dependencies": { 1553 | "debug": "2.6.9", 1554 | "depd": "2.0.0", 1555 | "destroy": "1.2.0", 1556 | "encodeurl": "~1.0.2", 1557 | "escape-html": "~1.0.3", 1558 | "etag": "~1.8.1", 1559 | "fresh": "0.5.2", 1560 | "http-errors": "2.0.0", 1561 | "mime": "1.6.0", 1562 | "ms": "2.1.3", 1563 | "on-finished": "2.4.1", 1564 | "range-parser": "~1.2.1", 1565 | "statuses": "2.0.1" 1566 | }, 1567 | "engines": { 1568 | "node": ">= 0.8.0" 1569 | } 1570 | }, 1571 | "node_modules/send/node_modules/ms": { 1572 | "version": "2.1.3", 1573 | "resolved": "https://registry.npmmirror.com/ms/-/ms-2.1.3.tgz", 1574 | "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" 1575 | }, 1576 | "node_modules/serve-static": { 1577 | "version": "1.15.0", 1578 | "resolved": "https://registry.npmmirror.com/serve-static/-/serve-static-1.15.0.tgz", 1579 | "integrity": "sha512-XGuRDNjXUijsUL0vl6nSD7cwURuzEgglbOaFuZM9g3kwDXOWVTck0jLzjPzGD+TazWbboZYu52/9/XPdUgne9g==", 1580 | "dependencies": { 1581 | "encodeurl": "~1.0.2", 1582 | "escape-html": "~1.0.3", 1583 | "parseurl": "~1.3.3", 1584 | "send": "0.18.0" 1585 | }, 1586 | "engines": { 1587 | "node": ">= 0.8.0" 1588 | } 1589 | }, 1590 | "node_modules/set-function-length": { 1591 | "version": "1.2.2", 1592 | "resolved": "https://registry.npmmirror.com/set-function-length/-/set-function-length-1.2.2.tgz", 1593 | "integrity": "sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==", 1594 | "dependencies": { 1595 | "define-data-property": "^1.1.4", 1596 | "es-errors": "^1.3.0", 1597 | "function-bind": "^1.1.2", 1598 | "get-intrinsic": "^1.2.4", 1599 | "gopd": "^1.0.1", 1600 | "has-property-descriptors": "^1.0.2" 1601 | }, 1602 | "engines": { 1603 | "node": ">= 0.4" 1604 | } 1605 | }, 1606 | "node_modules/setprototypeof": { 1607 | "version": "1.2.0", 1608 | "resolved": "https://registry.npmmirror.com/setprototypeof/-/setprototypeof-1.2.0.tgz", 1609 | "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==" 1610 | }, 1611 | "node_modules/side-channel": { 1612 | "version": "1.0.6", 1613 | "resolved": "https://registry.npmmirror.com/side-channel/-/side-channel-1.0.6.tgz", 1614 | "integrity": "sha512-fDW/EZ6Q9RiO8eFG8Hj+7u/oW+XrPTIChwCOM2+th2A6OblDtYYIpve9m+KvI9Z4C9qSEXlaGR6bTEYHReuglA==", 1615 | "dependencies": { 1616 | "call-bind": "^1.0.7", 1617 | "es-errors": "^1.3.0", 1618 | "get-intrinsic": "^1.2.4", 1619 | "object-inspect": "^1.13.1" 1620 | }, 1621 | "engines": { 1622 | "node": ">= 0.4" 1623 | }, 1624 | "funding": { 1625 | "url": "https://github.com/sponsors/ljharb" 1626 | } 1627 | }, 1628 | "node_modules/sift": { 1629 | "version": "13.5.2", 1630 | "resolved": "https://registry.npmmirror.com/sift/-/sift-13.5.2.tgz", 1631 | "integrity": "sha512-+gxdEOMA2J+AI+fVsCqeNn7Tgx3M9ZN9jdi95939l1IJ8cZsqS8sqpJyOkic2SJk+1+98Uwryt/gL6XDaV+UZA==" 1632 | }, 1633 | "node_modules/sliced": { 1634 | "version": "1.0.1", 1635 | "resolved": "https://registry.npmmirror.com/sliced/-/sliced-1.0.1.tgz", 1636 | "integrity": "sha512-VZBmZP8WU3sMOZm1bdgTadsQbcscK0UM8oKxKVBs4XAhUo2Xxzm/OFMGBkPusxw9xL3Uy8LrzEqGqJhclsr0yA==" 1637 | }, 1638 | "node_modules/source-map": { 1639 | "version": "0.6.1", 1640 | "resolved": "https://registry.npmmirror.com/source-map/-/source-map-0.6.1.tgz", 1641 | "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", 1642 | "dev": true, 1643 | "engines": { 1644 | "node": ">=0.10.0" 1645 | } 1646 | }, 1647 | "node_modules/source-map-support": { 1648 | "version": "0.5.21", 1649 | "resolved": "https://registry.npmmirror.com/source-map-support/-/source-map-support-0.5.21.tgz", 1650 | "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==", 1651 | "dev": true, 1652 | "dependencies": { 1653 | "buffer-from": "^1.0.0", 1654 | "source-map": "^0.6.0" 1655 | } 1656 | }, 1657 | "node_modules/sparse-bitfield": { 1658 | "version": "3.0.3", 1659 | "resolved": "https://registry.npmmirror.com/sparse-bitfield/-/sparse-bitfield-3.0.3.tgz", 1660 | "integrity": "sha512-kvzhi7vqKTfkh0PZU+2D2PIllw2ymqJKujUcyPMd9Y75Nv4nPbGJZXNhxsgdQab2BmlDct1YnfQCguEvHr7VsQ==", 1661 | "optional": true, 1662 | "dependencies": { 1663 | "memory-pager": "^1.0.2" 1664 | } 1665 | }, 1666 | "node_modules/statuses": { 1667 | "version": "2.0.1", 1668 | "resolved": "https://registry.npmmirror.com/statuses/-/statuses-2.0.1.tgz", 1669 | "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==", 1670 | "engines": { 1671 | "node": ">= 0.8" 1672 | } 1673 | }, 1674 | "node_modules/string_decoder": { 1675 | "version": "1.1.1", 1676 | "resolved": "https://registry.npmmirror.com/string_decoder/-/string_decoder-1.1.1.tgz", 1677 | "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", 1678 | "dependencies": { 1679 | "safe-buffer": "~5.1.0" 1680 | } 1681 | }, 1682 | "node_modules/string_decoder/node_modules/safe-buffer": { 1683 | "version": "5.1.2", 1684 | "resolved": "https://registry.npmmirror.com/safe-buffer/-/safe-buffer-5.1.2.tgz", 1685 | "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" 1686 | }, 1687 | "node_modules/strip-bom": { 1688 | "version": "3.0.0", 1689 | "resolved": "https://registry.npmmirror.com/strip-bom/-/strip-bom-3.0.0.tgz", 1690 | "integrity": "sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==", 1691 | "dev": true, 1692 | "engines": { 1693 | "node": ">=4" 1694 | } 1695 | }, 1696 | "node_modules/strip-json-comments": { 1697 | "version": "2.0.1", 1698 | "resolved": "https://registry.npmmirror.com/strip-json-comments/-/strip-json-comments-2.0.1.tgz", 1699 | "integrity": "sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ==", 1700 | "dev": true, 1701 | "engines": { 1702 | "node": ">=0.10.0" 1703 | } 1704 | }, 1705 | "node_modules/supports-preserve-symlinks-flag": { 1706 | "version": "1.0.0", 1707 | "resolved": "https://registry.npmmirror.com/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", 1708 | "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", 1709 | "dev": true, 1710 | "engines": { 1711 | "node": ">= 0.4" 1712 | }, 1713 | "funding": { 1714 | "url": "https://github.com/sponsors/ljharb" 1715 | } 1716 | }, 1717 | "node_modules/to-regex-range": { 1718 | "version": "5.0.1", 1719 | "resolved": "https://registry.npmmirror.com/to-regex-range/-/to-regex-range-5.0.1.tgz", 1720 | "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", 1721 | "dev": true, 1722 | "dependencies": { 1723 | "is-number": "^7.0.0" 1724 | }, 1725 | "engines": { 1726 | "node": ">=8.0" 1727 | } 1728 | }, 1729 | "node_modules/toidentifier": { 1730 | "version": "1.0.1", 1731 | "resolved": "https://registry.npmmirror.com/toidentifier/-/toidentifier-1.0.1.tgz", 1732 | "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==", 1733 | "engines": { 1734 | "node": ">=0.6" 1735 | } 1736 | }, 1737 | "node_modules/tree-kill": { 1738 | "version": "1.2.2", 1739 | "resolved": "https://registry.npmmirror.com/tree-kill/-/tree-kill-1.2.2.tgz", 1740 | "integrity": "sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A==", 1741 | "dev": true, 1742 | "bin": { 1743 | "tree-kill": "cli.js" 1744 | } 1745 | }, 1746 | "node_modules/ts-node": { 1747 | "version": "10.9.2", 1748 | "resolved": "https://registry.npmmirror.com/ts-node/-/ts-node-10.9.2.tgz", 1749 | "integrity": "sha512-f0FFpIdcHgn8zcPSbf1dRevwt047YMnaiJM3u2w2RewrB+fob/zePZcrOyQoLMMO7aBIddLcQIEK5dYjkLnGrQ==", 1750 | "dev": true, 1751 | "dependencies": { 1752 | "@cspotcode/source-map-support": "^0.8.0", 1753 | "@tsconfig/node10": "^1.0.7", 1754 | "@tsconfig/node12": "^1.0.7", 1755 | "@tsconfig/node14": "^1.0.0", 1756 | "@tsconfig/node16": "^1.0.2", 1757 | "acorn": "^8.4.1", 1758 | "acorn-walk": "^8.1.1", 1759 | "arg": "^4.1.0", 1760 | "create-require": "^1.1.0", 1761 | "diff": "^4.0.1", 1762 | "make-error": "^1.1.1", 1763 | "v8-compile-cache-lib": "^3.0.1", 1764 | "yn": "3.1.1" 1765 | }, 1766 | "bin": { 1767 | "ts-node": "dist/bin.js", 1768 | "ts-node-cwd": "dist/bin-cwd.js", 1769 | "ts-node-esm": "dist/bin-esm.js", 1770 | "ts-node-script": "dist/bin-script.js", 1771 | "ts-node-transpile-only": "dist/bin-transpile.js", 1772 | "ts-script": "dist/bin-script-deprecated.js" 1773 | }, 1774 | "peerDependencies": { 1775 | "@swc/core": ">=1.2.50", 1776 | "@swc/wasm": ">=1.2.50", 1777 | "@types/node": "*", 1778 | "typescript": ">=2.7" 1779 | }, 1780 | "peerDependenciesMeta": { 1781 | "@swc/core": { 1782 | "optional": true 1783 | }, 1784 | "@swc/wasm": { 1785 | "optional": true 1786 | } 1787 | } 1788 | }, 1789 | "node_modules/ts-node-dev": { 1790 | "version": "1.1.8", 1791 | "resolved": "https://registry.npmmirror.com/ts-node-dev/-/ts-node-dev-1.1.8.tgz", 1792 | "integrity": "sha512-Q/m3vEwzYwLZKmV6/0VlFxcZzVV/xcgOt+Tx/VjaaRHyiBcFlV0541yrT09QjzzCxlDZ34OzKjrFAynlmtflEg==", 1793 | "dev": true, 1794 | "dependencies": { 1795 | "chokidar": "^3.5.1", 1796 | "dynamic-dedupe": "^0.3.0", 1797 | "minimist": "^1.2.5", 1798 | "mkdirp": "^1.0.4", 1799 | "resolve": "^1.0.0", 1800 | "rimraf": "^2.6.1", 1801 | "source-map-support": "^0.5.12", 1802 | "tree-kill": "^1.2.2", 1803 | "ts-node": "^9.0.0", 1804 | "tsconfig": "^7.0.0" 1805 | }, 1806 | "bin": { 1807 | "ts-node-dev": "lib/bin.js", 1808 | "tsnd": "lib/bin.js" 1809 | }, 1810 | "engines": { 1811 | "node": ">=0.8.0" 1812 | }, 1813 | "peerDependencies": { 1814 | "node-notifier": "*", 1815 | "typescript": "*" 1816 | }, 1817 | "peerDependenciesMeta": { 1818 | "node-notifier": { 1819 | "optional": true 1820 | } 1821 | } 1822 | }, 1823 | "node_modules/ts-node-dev/node_modules/ts-node": { 1824 | "version": "9.1.1", 1825 | "resolved": "https://registry.npmmirror.com/ts-node/-/ts-node-9.1.1.tgz", 1826 | "integrity": "sha512-hPlt7ZACERQGf03M253ytLY3dHbGNGrAq9qIHWUY9XHYl1z7wYngSr3OQ5xmui8o2AaxsONxIzjafLUiWBo1Fg==", 1827 | "dev": true, 1828 | "dependencies": { 1829 | "arg": "^4.1.0", 1830 | "create-require": "^1.1.0", 1831 | "diff": "^4.0.1", 1832 | "make-error": "^1.1.1", 1833 | "source-map-support": "^0.5.17", 1834 | "yn": "3.1.1" 1835 | }, 1836 | "bin": { 1837 | "ts-node": "dist/bin.js", 1838 | "ts-node-script": "dist/bin-script.js", 1839 | "ts-node-transpile-only": "dist/bin-transpile.js", 1840 | "ts-script": "dist/bin-script-deprecated.js" 1841 | }, 1842 | "engines": { 1843 | "node": ">=10.0.0" 1844 | }, 1845 | "peerDependencies": { 1846 | "typescript": ">=2.7" 1847 | } 1848 | }, 1849 | "node_modules/tsconfig": { 1850 | "version": "7.0.0", 1851 | "resolved": "https://registry.npmmirror.com/tsconfig/-/tsconfig-7.0.0.tgz", 1852 | "integrity": "sha512-vZXmzPrL+EmC4T/4rVlT2jNVMWCi/O4DIiSj3UHg1OE5kCKbk4mfrXc6dZksLgRM/TZlKnousKH9bbTazUWRRw==", 1853 | "dev": true, 1854 | "dependencies": { 1855 | "@types/strip-bom": "^3.0.0", 1856 | "@types/strip-json-comments": "0.0.30", 1857 | "strip-bom": "^3.0.0", 1858 | "strip-json-comments": "^2.0.0" 1859 | } 1860 | }, 1861 | "node_modules/type-is": { 1862 | "version": "1.6.18", 1863 | "resolved": "https://registry.npmmirror.com/type-is/-/type-is-1.6.18.tgz", 1864 | "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", 1865 | "dependencies": { 1866 | "media-typer": "0.3.0", 1867 | "mime-types": "~2.1.24" 1868 | }, 1869 | "engines": { 1870 | "node": ">= 0.6" 1871 | } 1872 | }, 1873 | "node_modules/typescript": { 1874 | "version": "5.4.5", 1875 | "resolved": "https://registry.npmmirror.com/typescript/-/typescript-5.4.5.tgz", 1876 | "integrity": "sha512-vcI4UpRgg81oIRUFwR0WSIHKt11nJ7SAVlYNIu+QpqeyXP+gpQJy/Z4+F0aGxSE4MqwjyXvW/TzgkLAx2AGHwQ==", 1877 | "dev": true, 1878 | "bin": { 1879 | "tsc": "bin/tsc", 1880 | "tsserver": "bin/tsserver" 1881 | }, 1882 | "engines": { 1883 | "node": ">=14.17" 1884 | } 1885 | }, 1886 | "node_modules/unpipe": { 1887 | "version": "1.0.0", 1888 | "resolved": "https://registry.npmmirror.com/unpipe/-/unpipe-1.0.0.tgz", 1889 | "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==", 1890 | "engines": { 1891 | "node": ">= 0.8" 1892 | } 1893 | }, 1894 | "node_modules/util-deprecate": { 1895 | "version": "1.0.2", 1896 | "resolved": "https://registry.npmmirror.com/util-deprecate/-/util-deprecate-1.0.2.tgz", 1897 | "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==" 1898 | }, 1899 | "node_modules/utils-merge": { 1900 | "version": "1.0.1", 1901 | "resolved": "https://registry.npmmirror.com/utils-merge/-/utils-merge-1.0.1.tgz", 1902 | "integrity": "sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==", 1903 | "engines": { 1904 | "node": ">= 0.4.0" 1905 | } 1906 | }, 1907 | "node_modules/v8-compile-cache-lib": { 1908 | "version": "3.0.1", 1909 | "resolved": "https://registry.npmmirror.com/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz", 1910 | "integrity": "sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==", 1911 | "dev": true 1912 | }, 1913 | "node_modules/vary": { 1914 | "version": "1.1.2", 1915 | "resolved": "https://registry.npmmirror.com/vary/-/vary-1.1.2.tgz", 1916 | "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==", 1917 | "engines": { 1918 | "node": ">= 0.8" 1919 | } 1920 | }, 1921 | "node_modules/wrappy": { 1922 | "version": "1.0.2", 1923 | "resolved": "https://registry.npmmirror.com/wrappy/-/wrappy-1.0.2.tgz", 1924 | "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", 1925 | "dev": true 1926 | }, 1927 | "node_modules/xtend": { 1928 | "version": "4.0.2", 1929 | "resolved": "https://registry.npmmirror.com/xtend/-/xtend-4.0.2.tgz", 1930 | "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==", 1931 | "dev": true, 1932 | "engines": { 1933 | "node": ">=0.4" 1934 | } 1935 | }, 1936 | "node_modules/yn": { 1937 | "version": "3.1.1", 1938 | "resolved": "https://registry.npmmirror.com/yn/-/yn-3.1.1.tgz", 1939 | "integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==", 1940 | "dev": true, 1941 | "engines": { 1942 | "node": ">=6" 1943 | } 1944 | } 1945 | } 1946 | } 1947 | -------------------------------------------------------------------------------- /server/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "server", 3 | "version": "1.0.0", 4 | "main": "index.js", 5 | "scripts": { 6 | "start": "ts-node src/index.ts", 7 | "dev": "ts-node-dev src/index.ts" 8 | }, 9 | "dependencies": { 10 | "bcryptjs": "^2.4.3", 11 | "cors": "^2.8.5", 12 | "express": "^4.17.1", 13 | "jsonwebtoken": "^8.5.1", 14 | "mongoose": "^5.13.5" 15 | }, 16 | "devDependencies": { 17 | "@types/bcryptjs": "^2.4.2", 18 | "@types/cors": "^2.8.17", 19 | "@types/express": "^4.17.13", 20 | "@types/jsonwebtoken": "^8.5.0", 21 | "@types/mongoose": "^5.11.97", 22 | "@types/node": "^16.7.13", 23 | "ts-node": "^10.2.1", 24 | "ts-node-dev": "^1.1.6", 25 | "typescript": "^5.4.5" 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /server/src/app.ts: -------------------------------------------------------------------------------- 1 | import express from 'express'; 2 | import mongoose, { ConnectOptions } from 'mongoose'; 3 | import cors from 'cors' 4 | import authRoutes from './routes/auth'; 5 | import articleRoutes from './routes/article'; 6 | import exchangeRateRoutes from './routes/exchangeRate'; 7 | 8 | const app = express(); 9 | 10 | app.use(express.json()); 11 | app.use(cors()); 12 | 13 | app.use('/api/auth', authRoutes); 14 | app.use('/api/articles', articleRoutes); 15 | app.use('/api/exchangeRates', exchangeRateRoutes); 16 | 17 | const startServer = async () => { 18 | try { 19 | await mongoose.connect('mongodb://localhost:27017/currencyeg', { 20 | useNewUrlParser: true, 21 | useUnifiedTopology: true, 22 | } as ConnectOptions); 23 | console.log('Connected to MongoDB'); 24 | 25 | app.listen(3000, () => { 26 | console.log('Server is running on port 3000'); 27 | }); 28 | } catch (error) { 29 | console.error('Error connecting to MongoDB', error); 30 | process.exit(1); 31 | } 32 | }; 33 | 34 | startServer(); 35 | -------------------------------------------------------------------------------- /server/src/controllers/articleController.ts: -------------------------------------------------------------------------------- 1 | import { Request, Response } from 'express'; 2 | import { Article } from '../models/Article'; 3 | 4 | interface AuthRequest extends Request { 5 | userId?: string; 6 | } 7 | 8 | export const getArticles = async (req: Request, res: Response) => { 9 | const articles = await Article.find(); 10 | res.json(articles); 11 | }; 12 | 13 | export const getArticle = async (req: Request, res: Response) => { 14 | const article = await Article.findById(req.params.id); 15 | res.json(article); 16 | }; 17 | 18 | export const createArticle = async (req: AuthRequest, res: Response) => { 19 | const { title, content, preview } = req.body; 20 | const article = new Article({ title, content, preview, createdBy: req.userId }); 21 | await article.save(); 22 | res.json(article); 23 | }; 24 | 25 | export const deleteArticle = async (req: AuthRequest, res: Response) => { 26 | const article = await Article.findByIdAndDelete(req.params.id); 27 | res.json(article); 28 | }; 29 | -------------------------------------------------------------------------------- /server/src/controllers/authController.ts: -------------------------------------------------------------------------------- 1 | import { Request, Response } from 'express'; 2 | import { User } from '../models/User'; 3 | import bcrypt from 'bcryptjs'; 4 | import jwt from 'jsonwebtoken'; 5 | 6 | const JWT_SECRET = 'your_jwt_secret'; 7 | const TOKEN_EXPIRATION = '30d'; 8 | 9 | export const register = async (req: Request, res: Response) => { 10 | const { username, password } = req.body; 11 | const existingUser = await User.findOne({ username }); 12 | if (existingUser) { 13 | return res.status(400).json({ message: 'Username already exists' }); 14 | } 15 | 16 | const hashedPassword = await bcrypt.hash(password, 10); 17 | const user = new User({ username, password: hashedPassword }); 18 | await user.save(); 19 | 20 | const token = jwt.sign({ userId: user._id }, JWT_SECRET, { expiresIn: TOKEN_EXPIRATION }); 21 | res.json({ token }); 22 | }; 23 | 24 | export const login = async (req: Request, res: Response) => { 25 | const { username, password } = req.body; 26 | const user = await User.findOne({ username }); 27 | if (!user || !await bcrypt.compare(password, user.password)) { 28 | return res.sendStatus(401); 29 | } 30 | 31 | const token = jwt.sign({ userId: user._id }, JWT_SECRET, { expiresIn: TOKEN_EXPIRATION }); 32 | res.json({ token }); 33 | }; 34 | -------------------------------------------------------------------------------- /server/src/controllers/exchangeRateController.ts: -------------------------------------------------------------------------------- 1 | // server/src/controllers/exchangeRateController.ts 2 | import { Request, Response } from 'express'; 3 | import { ExchangeRate } from '../models/ExchangeRate'; 4 | 5 | export const getExchangeRates = async (req: Request, res: Response) => { 6 | const rates = await ExchangeRate.find(); 7 | res.json(rates); 8 | }; 9 | 10 | export const createExchangeRate = async (req: Request, res: Response) => { 11 | const { fromCurrency, toCurrency, rate } = req.body; 12 | const exchangeRate = new ExchangeRate({ fromCurrency, toCurrency, rate }); 13 | await exchangeRate.save(); 14 | res.json(exchangeRate); 15 | }; 16 | -------------------------------------------------------------------------------- /server/src/index.ts: -------------------------------------------------------------------------------- 1 | // src/index.ts 2 | import './app'; 3 | -------------------------------------------------------------------------------- /server/src/middlewares/authMiddleware.ts: -------------------------------------------------------------------------------- 1 | import { Request, Response, NextFunction } from 'express'; 2 | import jwt from 'jsonwebtoken'; 3 | 4 | const JWT_SECRET = 'your_jwt_secret'; 5 | 6 | interface AuthRequest extends Request { 7 | userId?: string; 8 | } 9 | 10 | export const authMiddleware = (req: AuthRequest, res: Response, next: NextFunction) => { 11 | const token = req.header('Authorization')?.replace('Bearer ', ''); 12 | if (!token) { 13 | return res.sendStatus(401); 14 | } 15 | 16 | try { 17 | const decoded: any = jwt.verify(token, JWT_SECRET); 18 | req.userId = decoded.userId; 19 | next(); 20 | } catch (error) { 21 | res.sendStatus(401); 22 | } 23 | }; 24 | -------------------------------------------------------------------------------- /server/src/models/Article.ts: -------------------------------------------------------------------------------- 1 | import { Schema, model } from 'mongoose'; 2 | 3 | interface IArticle { 4 | title: string; 5 | content: string; 6 | preview: string; 7 | createdBy: string; 8 | } 9 | 10 | const articleSchema = new Schema({ 11 | title: { type: String, required: true }, 12 | content: { type: String, required: true }, 13 | preview: { type: String, required: true }, 14 | createdBy: { type: String, required: true }, 15 | }); 16 | 17 | export const Article = model('Article', articleSchema); 18 | -------------------------------------------------------------------------------- /server/src/models/ExchangeRate.ts: -------------------------------------------------------------------------------- 1 | // server/src/models/ExchangeRate.ts 2 | import { Schema, model } from 'mongoose'; 3 | 4 | interface IExchangeRate { 5 | fromCurrency: string; 6 | toCurrency: string; 7 | rate: number; 8 | date: Date; 9 | } 10 | 11 | const exchangeRateSchema = new Schema({ 12 | fromCurrency: { type: String, required: true }, 13 | toCurrency: { type: String, required: true }, 14 | rate: { type: Number, required: true }, 15 | date: { type: Date, default: Date.now }, 16 | }); 17 | 18 | export const ExchangeRate = model('ExchangeRate', exchangeRateSchema); 19 | -------------------------------------------------------------------------------- /server/src/models/User.ts: -------------------------------------------------------------------------------- 1 | import { Schema, model } from 'mongoose'; 2 | 3 | interface IUser { 4 | username: string; 5 | password: string; 6 | } 7 | 8 | const userSchema = new Schema({ 9 | username: { type: String, required: true, unique: true }, 10 | password: { type: String, required: true }, 11 | }); 12 | 13 | export const User = model('User', userSchema); 14 | -------------------------------------------------------------------------------- /server/src/routes/article.ts: -------------------------------------------------------------------------------- 1 | import { Router } from 'express'; 2 | import { getArticles, getArticle, createArticle, deleteArticle } from '../controllers/articleController'; 3 | import { authMiddleware } from '../middlewares/authMiddleware'; 4 | 5 | const router = Router(); 6 | 7 | router.get('/', getArticles); 8 | router.get('/:id', getArticle); 9 | router.post('/', authMiddleware, createArticle); 10 | router.delete('/:id', authMiddleware, deleteArticle); 11 | 12 | export default router; 13 | -------------------------------------------------------------------------------- /server/src/routes/auth.ts: -------------------------------------------------------------------------------- 1 | import { Router } from 'express'; 2 | import { register, login } from '../controllers/authController'; 3 | 4 | const router = Router(); 5 | 6 | router.post('/register', register); 7 | router.post('/login', login); 8 | 9 | export default router; 10 | -------------------------------------------------------------------------------- /server/src/routes/exchangeRate.ts: -------------------------------------------------------------------------------- 1 | // server/src/routes/exchangeRate.ts 2 | import { Router } from 'express'; 3 | import { getExchangeRates, createExchangeRate } from '../controllers/exchangeRateController'; 4 | 5 | const router = Router(); 6 | 7 | router.get('/', getExchangeRates); 8 | router.post('/', createExchangeRate); 9 | 10 | export default router; 11 | -------------------------------------------------------------------------------- /server/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es2017", 4 | "module": "commonjs", 5 | "strict": true, 6 | "esModuleInterop": true, 7 | "skipLibCheck": true, 8 | "forceConsistentCasingInFileNames": true, 9 | "resolveJsonModule": true, 10 | "outDir": "./dist", 11 | "rootDir": "./src", 12 | "typeRoots": ["./node_modules/@types"] 13 | }, 14 | "include": ["src/**/*.ts"], 15 | "exclude": ["node_modules", "dist"] 16 | } 17 | -------------------------------------------------------------------------------- /vue感情参考書(课件).md: -------------------------------------------------------------------------------- 1 | # WEB 002 VUE-01 INKKAPLUM SH 2 | 3 | ## 前文 4 | 5 | WEB 002 VUE-01 6 | 7 | 本教程相比上一个的 TS 教程成熟了很多, 优势在于 8 | 9 | 1. 有一个大的 md 文件作为课件, 这样的话就避免了写代码浪费太多时间和讲的时候出现之前思路比较混乱的问题。 10 | 2. 提高了录屏质量, 录屏卡顿现象有所减少, 看起来会更加直观舒服。 11 | 3. 本机的配置, 之前的大写提示被关掉了, 所以不会因为大写提示导致大写字母没有办法被正常、快速地打出来 12 | 4. 插件的变更, 这一次把彩虹屁插件关掉了 13 | 5. Code 的变更, 这一次背景换成了 YOASOBI 的背景, 对比度会更高, 更加适合观看。 14 | 15 | 所以这一个教程的质量将会有提升, 同时依然满足了速成的要求。 16 | 17 | 录制视频不仅是传递知识和解法, 也是对于 Up 自身的一个挑战和知识的再加强, 所以如果有任何不懂的地方, 尤其是视频没有讲清楚的地方, 欢迎私信问 Up 主, Up 主会尽力给出一个可用的解法。然后 TypeScript 部分也是如此, 有任何觉得讲的不到位的地方, 欢迎私信, 会给出可用的解决方法。 18 | 19 | ## Vue 3 20 | 21 | ## 首先, 我们会用一定时间了解一下 Vue3 的新内容和新特点, 帮助快速上手 Vue 3。 22 | 23 | ### 学习 Vue3 的重要性 24 | 25 | 一句话, Vue2 已经 EOL 了, 所以必须要学习 Vue3。 26 | 27 | ### 变更内容 28 | 29 | 具体变更内容: 组合式 API,新钩子`setup()`, 用`reactive`和`ref`函数声明响应式变量。 30 | 31 | 剩余如`watch`监听器, `computed`计算属性写法都会有变化, 但是作用不变。 32 | 33 | 生命周期钩子也有一定的变化, 主要还是写法的变化。且有因为在大部分时候不需要用到`this`关键字, 所以模板引用也会有变化。 34 | 35 | 接下来就是组件间的传值语法也有一定变化。 36 | 37 | 而`template`和`style`则没有太多变化, 不需要记忆太多。 38 | 39 | 配套的工程化工具亦进行了更新 40 | 如 Pinia。因此我们不会涉及到 Vuex, 反之, 会讨论 Pinia。 41 | Pinia 作为一个状态管理工具, 更加高效精简, 更加易于维护。 42 | 43 | 此外, Vue2 是 Webpack 给项目进行一个打包, 而 Vue3 则会用 Vite。 44 | 45 | - Vue2 的 Vue-cli, 使用 vue-cli 时, 可以通过简单的命令(如 vue create project-name)来创建一个新的 Vue 项目。 46 | Vue-cli 内部集成了 webpack, 并且已经配置好了基本的 webpack 打包规则。这意味着当你使用 vue-cli 创建一个新的 Vue 项目时,你实际上也在使用 webpack 来构建和打包你的项目。 47 | 而 Vite 是一个由原生 ESM 驱动的 Web 开发构建工具。 总之速度就是有一个很大的提高。 48 | 49 | ## 底层逻辑 50 | 51 | 底层 API 变化: Vue2 响应式实现依赖于`object.defineProperty()`。但是有相当的效率问题。例子: 52 | 53 | ```ts 54 | data(){ 55 | return{ 56 | name: 'Inkka', 57 | age: 17 58 | } 59 | } 60 | ``` 61 | 62 | 时代局限: Vue2 创造时只有这一个方法可以劫持数据。 63 | 64 | 但是在 ES6 后 JS 新增了一个方法 Proxy(), 所以提高了性能。 65 | 66 | 语言支持: Vue2 底层源码使用 JavaScript 编写, 而 Vue3 则使用 TypeScript 编写, 理所应当地, Vue 3 增强了对于 TS 的支持。 67 | 68 | Diff 算法: Vue3 的 diff 算法和 vue2 不一样的, 参考专门的专栏。 69 | 70 | 打包后更小的体积: 更好的 TreeShaking。 71 | 72 | ### 选项式 API 和组合式 API 73 | 74 | 选项式 API 75 | 76 | ```vue 77 | 89 | ``` 90 | 91 | 先装一个插件, Vue VSCode Snippets, 这个插件将大大提高我们写代码的效率。 92 | 93 | 我们开始创建一个项目 94 | 95 | vue2 命令 96 | 97 | ```bash 98 | vue create ... 99 | ``` 100 | 101 | vue3 命令 102 | 103 | ```bash 104 | npm init vue@latest 105 | ``` 106 | 107 | 选项 108 | 109 | ```bash 110 | Ok to proceed 111 | Add TypeScript >> No 112 | Add JSX Support >> No 113 | Add Vue Router... >> No 114 | Add Pinia... >> No 115 | Add Vitest... >> No 116 | Add Testing Solution... >> No 117 | Add DevTools.. >> No 118 | ``` 119 | 120 | 打开`Package.json`, 命令是`npm run dev`。 121 | 122 | 然后我们`npm i`一下, 因为这个文件并没有 node_modules。 123 | 124 | 简单地看一看文件架构, 看看有什么变化? 125 | 126 | Vue 2 127 | 128 | ```js 129 | import Vue from 'vue' 130 | new Vue({ 131 | render: h => h(App) 132 | }).$mount('#app') 133 | 134 | //Or 135 | new Vue({ 136 | el: '#app' 137 | render: h => h(App) 138 | }) 139 | ``` 140 | 141 | Vue 3 142 | 143 | ```js 144 | import { createApp } from "vue"; 145 | createApp(App).mount("#app"); 146 | ``` 147 | 148 | Vue 3 的代码布局: 149 | 150 | ```vue 151 | 155 | 156 | 157 | ``` 158 | 159 | ### 体验组合式 API 160 | 161 | 首先我们用选项式 API 写一个计数器, 然后用组合式 API 再写一下。 162 | 163 | ```vue 164 | 165 | 170 | 171 | 205 | ``` 206 | 207 | 优势: 所有业务逻辑都写在一个地方, 不用在`data`,`method`内找了, 逻辑集中带来了极好的提升。 208 | 209 | 当然, `beforeCreate()`依然可以写在 Vue 3 内, 我们顺便看看`setup`的时机。 210 | 211 | 体验 Setup 语法糖 212 | 213 | ```vue 214 | 221 | ``` 222 | 223 | computed 语法变化 224 | 225 | ```js 226 | computed:{ 227 | testExample(){ 228 | return this.example * 114514 229 | } 230 | } 231 | ``` 232 | 233 | 新语法: 234 | 235 | ```js 236 | const homoCount = computed(() => { 237 | return objCount.value.count * 114514; 238 | }); 239 | ``` 240 | 241 | 监视数据- Watch 242 | 243 | ```js 244 | watch(objCount, (newVal, oldVal) => { 245 | console.log(`值从${oldVal}变成了${newVal}`); 246 | }); 247 | ``` 248 | 249 | 监听多个内容 250 | 251 | ```js 252 | const objCount = ref(0); 253 | const age = ref(114); 254 | 255 | watch( 256 | [objCount, age], 257 | (newVal, oldVal) => { 258 | console.log(`值从${oldVal}变成了${newVal}`); 259 | }, 260 | { immediate: true } 261 | ); 262 | ``` 263 | 264 | 深度监听 265 | 266 | ```js 267 | import { ref, watch } from "vue"; 268 | const objCount = ref({ 269 | count: 0, 270 | }); 271 | 272 | watch( 273 | objCount, 274 | (newVal, oldVal) => { 275 | console.log(oldVal); 276 | console.log(newVal); 277 | }, 278 | { deep: true } 279 | ); 280 | ``` 281 | 282 | 用深度克隆解决问题(供参考) 283 | 284 | ```js 285 | import { ref, watch } from "vue"; 286 | const objCount = ref({ 287 | count: 0, 288 | }); 289 | 290 | let oldState = JSON.parse(JSON.stringify(objCount.value)); 291 | 292 | watch( 293 | objCount, 294 | (newVal) => { 295 | console.log(oldState); 296 | console.log(newVal); 297 | oldState = JSON.parse(JSON.stringify(newVal)); 298 | }, 299 | { deep: true } 300 | ); 301 | ``` 302 | 303 | ### 很多钩子 304 | 305 | `beforeMount = > onBeforeMount`, `mounted => onMounted`, `beforeUpdate => onBeforeUpdate`, 306 | `updated => onUpdated`, `beforeDestroy => onBeforeUnmount`, `destroyed => onUnmounted` 307 | 308 | 体验语法 309 | 310 | ```vue 311 | 329 | ``` 330 | 331 | 注意一点, 这个视频中未提及到: 332 | 333 | 生命周期钩子可以执行多次, 按照顺序执行。 334 | 335 | ```js 336 | 337 | import { onMounted } from 'vue' 338 | onMounted(()=>{ 339 | }) 340 | onMounted(()=>{ 341 | }) 342 | 343 | ``` 344 | 345 | 利点: 如果一个钩子内逻辑很复杂, 改起来很烦, 可以写一个新的。 346 | 347 | ### 组件通信 348 | 349 | 子组件例子: 350 | 351 | ```vue 352 | 355 | 356 | 357 | 358 | 378 | ``` 379 | 380 | vue2 写法 381 | 382 | ```js 383 | $emit('example','content') 384 | 385 | //父组件 386 | @example="Method" 387 | 388 | //script 389 | const Method = (val) =>{ 390 | console.log(val) 391 | } 392 | ``` 393 | 394 | 案例代码(子组件内) 395 | 396 | ```html 397 | 398 | ``` 399 | 400 | 成品代码(父组件给子组件传值) 401 | 402 | 父组件 403 | 404 | ```vue 405 | 410 | 411 | 417 | ``` 418 | 419 | 子组件 420 | 421 | ```vue 422 | 428 | 429 | 437 | ``` 438 | 439 | 成品代码(子组件给父组件传值) 440 | 441 | 父组件: 442 | 443 | ```vue 444 | 451 | 452 | 458 | ``` 459 | 460 | 子组件: 461 | 462 | ```vue 463 | 471 | 472 | 485 | ``` 486 | 487 | ref 的写法 488 | 489 | 父组件 490 | 491 | ```vue 492 | 504 | 505 | 512 | 513 | 515 | ``` 516 | 517 | 子组件 518 | 519 | ```js 520 | 528 | 529 | 556 | ``` 557 | 558 | 注: 模板引用 559 | 560 | 通过ref标识获得真实的dom对象或者组件的实例对象。 561 | 562 | 案例: 实现聚焦 563 | 模板代码 564 | 565 | ```vue 566 | 569 | ``` 570 | 571 | `Provide`和`inject`实现组件之间传值 572 | 573 | App.vue 574 | 575 | ```vue 576 | 577 | 578 | 583 | 584 | 593 | ``` 594 | 595 | ComponentsTest02.vue [孙组件] 596 | 597 | ```vue 598 | 599 | 604 | 605 | 611 | ``` 612 | 613 | ComponentsTest03.vue [父组件] 614 | 615 | ```vue 616 | 618 | 619 | 624 | 625 | 631 | ``` 632 | 633 | ## 和 TS 结合开发 634 | 635 | ### 为什么要用 TS? 636 | 637 | 静态类型检查可以提前第发现代码错误, 加了类型只要不符合规范就会报错, 提前地发现错误。 638 | 639 | 我们用新的命令去创建 TS 项目。 640 | 641 | ```bash 642 | npm create vite@latest 项目名 -- --template vanilla-ts 643 | ``` 644 | 645 | 用最新的 vite 版本来创建一个以原生 TypeScript 为模板的项目。 646 | 647 | 我们简单地审视文件, 可以看到依然没有 node modules, 所以我们需要自己装一下 648 | 649 | ```bash 650 | npm i 651 | ``` 652 | 653 | 清空对应文件, 写一些测试代码 654 | 655 | ```ts 656 | let test03: number = 114514; 657 | console.log(test03); 658 | ``` 659 | 660 | 让每个文件都是一个单独的模块 661 | 662 | ```ts 663 | export {}; 664 | ``` 665 | 666 | 体验 TS 接口, 接口如何处理对象多个层级的情况。 667 | 例子: 668 | 669 | ```ts 670 | const uta = { 671 | id: 1, 672 | producer: "harumakigohan", 673 | info: { 674 | title: "メルティランドナイトメア", 675 | lyrics: "Welcome to the MeltyLand", 676 | }, 677 | }; 678 | ``` 679 | 680 | 请用接口帮助完成类型的注解。 681 | 682 | 用断言帮助智能提示: 683 | 684 | ```ts 685 | const TestId = document.getElementById("#test") as HTMLImageElement; 686 | ``` 687 | 688 | ## 正式上手 Vue+ts 开发 689 | 690 | ```bash 691 | npm create vite@latest 项目名 -- --template vue-ts 692 | ``` 693 | 694 | 创建一个以 vue-ts 为模板的项目 695 | 696 | 接下来会让你 select 一个框架, 选择 vue 即可, 后面 variant 依然选 TypeScript。 697 | 698 | ### 给 ref 添加类型标记。 699 | 700 | ```ts 701 | const year = ref(1145) 702 | ``` 703 | 704 | ### ref, reactive, computed 综合代码 705 | 706 | computed 不用手动添加类型, 会自动地推到出来。 707 | 708 | 处理复杂类型 709 | 710 | ```js 711 | [ 712 | { id: "001", name: "USD", rate: 1 }, 713 | { id: "002", name: "RUB", rate: 90 }, 714 | { id: "003", name: "KZT", rate: 440 }, 715 | { id: "004", name: "SGD", rate: 1.3 }, 716 | ]; 717 | ``` 718 | 719 | 请筛选出大于 60 的。 720 | 721 | ```ts 722 | const newList = computed(() => { 723 | return Currency.filter((el) => el.rate > 60); 724 | }); 725 | ``` 726 | 727 | 触发事件时的类型 728 | 729 | 需求: 请输入文本同时在控制台输出输入的内容。 730 | 731 | ```vue 732 | 733 | ``` 734 | 735 | 需求: 当点击按钮时, 请输出按钮对应的内容=> `按钮内容` 736 | 737 | ```vue 738 | 739 | ``` 740 | 741 | ### 模板引用类型 742 | 743 | 还是经典案例, 我们实现聚焦工作。 744 | 下文是原来的版本: 745 | 746 | ```vue 747 | 750 | 751 | 766 | ``` 767 | 768 | 案例代码: 769 | 770 | 父组件 771 | 772 | ```vue 773 | 778 | 779 | 782 | ``` 783 | 784 | 子组件 785 | 786 | ```vue 787 | 793 | 794 | 802 | ``` 803 | 804 | 父组件 805 | 806 | ```vue 807 | 808 | 814 | 815 | 817 | ``` 818 | 819 | 提示: 空值处理(`null`), 一般地, 我们有很多处理方法, 一种就是用`?`可选链运算符, 820 | 还有就是用 if 语句, 如上文例子, 也就是逻辑判断 821 | 822 | ```ts 823 | if (logMsgRef.value) { 824 | logMsgRef.value.logMessage(); 825 | } 826 | ``` 827 | 828 | 当然, 也可以用非空断言运算符, 加一个`!`即可, 但是不建议。 829 | 830 | ### 传值写法 831 | 832 | 继续基于原来的代码 833 | 834 | 提示: 835 | 好处: 报错提前, 有智能提示 836 | 837 | ```js 838 | defineProps({ 839 | msg: { 840 | type: String, 841 | required: true, 842 | }, 843 | }); 844 | ``` 845 | 846 | ```ts 847 | type Props = { 848 | ... 849 | } 850 | ``` 851 | 852 | 添加默认值 853 | 854 | 用`withDefaults`即可。 855 | 856 | withDefaults(第一个参数: defineProps 内容, 第二个参数: 默认值配置项) 857 | 858 | ```ts 859 | const props = withDefaults(defineProps(), { 860 | value: "200", 861 | }); 862 | ``` 863 | 864 | 子传父例 865 | 866 | 提示: 867 | 好处: 报错提前, 有智能提示 868 | 869 | ```ts 870 | type Emits = { 871 | (e: "msg", msg: string): void; 872 | }; 873 | const Emit = defineEmits(); 874 | const handleClick = () => { 875 | Emit("msg", "testVal"); 876 | }; 877 | ``` 878 | 879 | ## 解决问题: 第三方包想要有类型约束怎么办? 880 | 881 | ```bash 882 | npm i --save-dev @types/jquery 883 | ``` 884 | 885 | 自己写类型文件 886 | 887 | ```ts 888 | export type Currency = { 889 | name: string; 890 | }; 891 | ``` 892 | 893 | ## Pinia 894 | 895 | 新变化: 更加符合直觉, 去掉了 mutation。提供组合式风格 API, 去掉了 Modules 的概念, 自动地分配模块。对于 TypeScript 更加友好 896 | 897 | ```bash 898 | npm install pinia 899 | ``` 900 | 901 | ```ts 902 | import { createApp } from "vue"; 903 | import { createPinia } from "pinia"; 904 | import App from "./App.vue"; 905 | const pinia = createPinia(); 906 | const app = createApp(App); 907 | app.use(pinia); 908 | app.mount("#app"); 909 | ``` 910 | 911 | vuex 定义仓库 912 | 913 | ```js 914 | const store = createStore({ 915 | //... 916 | }); 917 | ``` 918 | 919 | pinia 920 | 921 | ```js 922 | import { defineStore } from "pinia"; 923 | defineStore("counter", () => {}); 924 | ``` 925 | 926 | ```ts 927 | import { defineStore } from "pinia"; 928 | 929 | // 你可以任意命名 `defineStore()` 的返回值,但最好使用 store 的名字,同时以 `use` 开头且以 `Store` 结尾。 930 | // (比如 `useUserStore`,`useCartStore`,`useProductStore`) 931 | // 第一个参数是你的应用中 Store 的唯一 ID。 932 | export const useAlertsStore = defineStore("alerts", { 933 | // 其他配置... 934 | }); 935 | ``` 936 | 937 | state 被定义为一个返回初始状态的函数 938 | 939 | `actions => methods` 940 | 941 | `getters` => `computed`函数模拟 942 | 943 | ```ts 944 | const doubleCount = computed(() => { 945 | return count.value * 2; 946 | }); 947 | ``` 948 | 949 | ## 附录 950 | 951 | ### 莫名其妙的报错 952 | 953 | 莫名其妙的报错指编辑器总是说找不到文件/对应的类型声明, 这个时候修改 tsconfig 就很重要了。然后重新启动(`npm run dev`)直接用下面的即可, 假如依然不行, 私信联系 UP 主即可, 因为还有几个对策, 但是没有写出来。 954 | 955 | vue 文件的类型声明 shims-vue.d.ts 956 | 957 | ```ts 958 | declare module "*.vue" { 959 | import { DefineComponent } from "vue"; 960 | const component: DefineComponent<{}, {}, any>; 961 | export default component; 962 | } 963 | ``` 964 | 965 | ```json 966 | { 967 | "compilerOptions": { 968 | "target": "esnext", 969 | "module": "esnext", 970 | "strict": true, 971 | "jsx": "preserve", 972 | "moduleResolution": "node", 973 | "resolveJsonModule": true, 974 | "esModuleInterop": true, 975 | "skipLibCheck": true, 976 | "forceConsistentCasingInFileNames": true, 977 | "baseUrl": ".", 978 | "paths": { 979 | "@/*": ["src/*"] 980 | }, 981 | "types": ["vite/client"] 982 | }, 983 | "include": ["src/**/*.ts", "src/**/*.d.ts", "src/**/*.tsx", "src/**/*.vue"] 984 | } 985 | ``` 986 | 987 | 接下来我们将做一个简单的项目, 来强化知识点。 988 | 989 | 项目实战我们因为时间关系, 所以做一个简单的小程序, 就是查汇率, 查看新闻的一个简单程序 990 | 这个项目其实是不够过关的, 所以建议之后学习如聊天 app, 还有传统的如人资后台, 外卖软件等等。 991 | 那么在 Up 主以后有需要做这样的项目时, Up主也会录出来 有可能是一个论坛+博客网站 但是后端肯定是基于 go 的。 992 | -------------------------------------------------------------------------------- /前端代码说明.md: -------------------------------------------------------------------------------- 1 | # 前端代码说明 2 | 3 | 此说明为 B 站: InkkaPlum 频道的相关教程所用, 仅供学习。不得二次用于任何机构/个人再次录制 Vue/Node/Element 或其它任何语言, 框架, 架构, 工具等等教程中。 4 | 5 | 测试代码需要自己配置输入对应命令配置对应环境! 6 | 7 | ```bash 8 | npm i 9 | npm install pinia element-plus axios --save 10 | npm install vue-router@4 11 | ``` 12 | 13 | ## 欢迎改进成一个更加可扩展的项目 14 | 15 | 之前说明指出了不可商用, 但是非常欢迎修改项目或者添加更多功能。这个项目只是实现了基本的功能, 也有很多未完成的点。 16 | 17 | 能否利用相关库, 自己实现 API, 让其展示最近的汇率走势。 18 | 19 | 文章能否实现收藏功能, 或者点赞功能, 当然更远地, 能否完成评论的发布和投稿功能的实现。 20 | 21 | 此外, 需要让该站点变得更安全需要更多的改变, 感兴趣欢迎以此项目为媒介探索 Vue 乃至后端的开发。 22 | 23 | ## 架构 24 | 25 | ```html 26 | client/ |-- src/ | |-- assets/ | |-- components/ | | |-- Login.vue | | |-- 27 | Register.vue | |-- router/ | | |-- index.ts | |-- store/ | | |-- auth.ts | |-- 28 | views/ | | |-- CurrencyExchangeView.vue | | |-- HomeView.vue | | |-- 29 | NewsDetailView.vue | | |-- NewsView.vue | |-- App.vue | |-- axios.ts | |-- 30 | main.ts | |-- shims-vue.d.ts | |-- vite-env.d.ts |-- package.json |-- 31 | tsconfig.json |-- vite.config.ts |-- index.html 32 | ``` 33 | 34 | ## 配置过程 35 | 36 | 1. 创建项目 37 | 38 | 在 client 上一级目录, 如 myApp 输入 39 | 40 | ```bash 41 | npm create vite@latest client -- --template vue-ts 42 | ``` 43 | 44 | 然后`cd client`, 并且输入 45 | 46 | ```bash 47 | npm i 48 | ``` 49 | 50 | 2. 安装对应工具 51 | 52 | ```bash 53 | npm install pinia element-plus axios --save 54 | ``` 55 | 56 | `--save`将添加到 package.json 中的 Dependencies 里面。 57 | 58 | 同时, 需要一个 shims-vue.d.ts 文件为.vue 提供类型声明。 59 | 60 | ```ts 61 | declare module "*.vue" { 62 | import { DefineComponent } from "vue"; 63 | const component: DefineComponent<{}, {}, any>; 64 | export default component; 65 | } 66 | ``` 67 | 68 | 3. 我们首先看一下最后的实现结果, 并且完成导航栏的制作。(App.vue 内) 69 | 70 | 首先, 我们参考官方文档, main.ts 内 71 | 72 | 我们直接完整导入, 实际上而言建议按需导入, 可能更加好一点。但是这里我们为了减小时间, 直接地`import...` 73 | 74 | 这些直接看官方文档即可, 为了方便各位, 直接在这里导入就行。 75 | 76 | ```ts 77 | import { createApp } from "vue"; 78 | import App from "./App.vue"; 79 | import ElementPlus from "element-plus"; 80 | import "element-plus/dist/index.css"; 81 | 82 | const app = createApp(App); 83 | app.use(ElementPlus); 84 | app.mount("#app"); 85 | ``` 86 | 87 | 更多的环境配置直接看官网即可。 88 | 89 | ## 开始写代码! 90 | 91 | 首先, 我们需要挑选合适的组件做一个顶部导航栏 92 | 在 elementPlus 官网查看合适的方法: 93 | 94 | 官网=> 组件=> 下滑至导航=> menu 菜单。 95 | 96 | 这样地查看源代码: 97 | 98 | ![查看源代码](image-5.png) 99 | 100 | 解释: 101 | 102 | a. ellipsis 当你内容过多页面放不下的情况下就会有个三点形式的省略号。 103 | b. 需要有一个合适的容器。 104 | 105 | ```js 106 | 128 | 129 | 137 | ``` 138 | 139 | 然后, 我们配置路由, 首先让各个页面都有内容。基于目录架构, 在 views 文件中新建对应文件。 140 | 141 | 然后我们查看官方文档的案例 142 | 143 | index.js 144 | 145 | ```js 146 | import { createMemoryHistory, createRouter } from "vue-router"; 147 | 148 | import HomeView from "./HomeView.vue"; 149 | import AboutView from "./AboutView.vue"; 150 | 151 | const routes = [ 152 | { path: "/", component: HomeView }, 153 | { path: "/about", component: AboutView }, 154 | ]; 155 | 156 | const router = createRouter({ 157 | history: createMemoryHistory(), 158 | routes, 159 | }); 160 | ``` 161 | 162 | Router 实例由 createRouter 创建。我们首先就需要将这里的 createMemoryHistory()改成 createWebHistory() 163 | createWebHistory: 这是用于创建基于 HTML5 历史模式的路由器历史的函数。HTML5 历史模式使用浏览器的`history.pushState`和`history.replaceState` API 来管理 URL, 而不是使用传统的 URL 哈希(#)。 164 | 165 | 类型: RouteRecordRaw 166 | 167 | 最终参考 168 | 169 | - 这是最终代码, 如果直接复制会有问题, 请参考视频进行修改。 170 | 171 | ```ts 172 | import { createRouter, createWebHistory, RouteRecordRaw } from "vue-router"; 173 | import HomeView from "../views/HomeView.vue"; 174 | import CurrencyExchangeView from "../views/CurrencyExchangeView.vue"; 175 | import NewsView from "../views/NewsView.vue"; 176 | import NewsDetailView from "../views/NewsDetailView.vue"; 177 | import Login from "../components/Login.vue"; 178 | import Register from "../components/Register.vue"; 179 | 180 | const routes: RouteRecordRaw[] = [ 181 | { path: "/", name: "Home", component: HomeView }, 182 | { 183 | path: "/exchange", 184 | name: "CurrencyExchange", 185 | component: CurrencyExchangeView, 186 | }, 187 | { path: "/news", name: "News", component: NewsView }, 188 | { path: "/news/:id", name: "NewsDetail", component: NewsDetailView }, 189 | { path: "/login", name: "Login", component: Login }, 190 | { path: "/register", name: "Register", component: Register }, 191 | ]; 192 | 193 | const router = createRouter({ 194 | history: createWebHistory(), 195 | routes, 196 | }); 197 | ``` 198 | 199 | 按照官方文档, 在 app.vue 导入对应内容 200 | 201 | ```ts 202 | import { useRouter, useRoute } from "vue-router"; 203 | ``` 204 | 205 | 具体实现需要等完成了 HomeView.vue, CurrencyExchangeView.vue, 基本的页面查看功能后才可以继续完成配置。 206 | 207 | HomeView.vue 208 | 这个代码没有任何重要的地方, 直接复制即可 209 | 210 | ```vue 211 | 219 | 220 | 221 | ``` 222 | 223 | CurrencyExchangeView.vue 224 | 225 | 这个代码我们首先需要实现一个表单, 那么我们需要去 ElementPlus 官方文档上查看适合的选择。 226 | 我们找到 el-form 即可 227 | 228 | ```vue 229 | 294 | 295 | 314 | ``` 315 | 316 | 我们复制模板代码 317 | 318 | ```html 319 | 344 | ``` 345 | 346 | 配置 axios。 347 | 348 | ```bash 349 | npm i axios 350 | ``` 351 | 352 | 新建一个 axios.ts 文件。 353 | 354 | JWT token 攻略: 355 | 356 | ```js 357 | client.interceptors.request.use( 358 | (config) => { 359 | if (config.authorization !== false) { 360 | const token = getCurrentAccessToken(); 361 | if (token) { 362 | config.headers.Authorization = "Bearer " + token; 363 | } 364 | } 365 | return config; 366 | }, 367 | (error) => { 368 | return Promise.reject(error); 369 | } 370 | ); 371 | ``` 372 | 373 | 最终代码: 374 | 375 | ```ts 376 | import axios from "axios"; 377 | 378 | const instance = axios.create({ 379 | baseURL: "http://localhost:3000/api", 380 | }); 381 | 382 | instance.interceptors.request.use((config) => { 383 | const token = localStorage.getItem("token"); 384 | if (token) { 385 | config.headers.Authorization = "Bearer " + token; 386 | } 387 | return config; 388 | }); 389 | 390 | export default instance; 391 | ``` 392 | 393 | 同时改变 vite.config.ts 解决跨域问题 394 | 395 | ```ts 396 | import { defineConfig } from "vite"; 397 | import vue from "@vitejs/plugin-vue"; 398 | 399 | export default defineConfig({ 400 | plugins: [vue()], 401 | server: { 402 | proxy: { 403 | "/api": { 404 | target: "http://localhost:3000", 405 | changeOrigin: true, 406 | secure: false, 407 | rewrite: (path) => path.replace(/^\/api/, ""), 408 | }, 409 | }, 410 | }, 411 | }); 412 | ``` 413 | 414 | 最终代码: 415 | 416 | ```ts 417 | import { ref, onMounted } from "vue"; 418 | import axios from "../axios"; 419 | 420 | interface ExchangeRate { 421 | fromCurrency: string; 422 | toCurrency: string; 423 | rate: number; 424 | } 425 | 426 | const form = ref({ 427 | fromCurrency: "", 428 | toCurrency: "", 429 | amount: 0, 430 | }); 431 | 432 | const result = ref(null); 433 | const currencies = ref([]); 434 | const rates = ref([]); 435 | 436 | const fetchCurrencies = async () => { 437 | const response = await axios.get("/exchangeRates"); 438 | rates.value = response.data; 439 | currencies.value = [ 440 | ...new Set( 441 | response.data 442 | .map((rate: ExchangeRate) => [rate.fromCurrency, rate.toCurrency]) 443 | .flat() 444 | ), 445 | ]; 446 | }; 447 | 448 | const exchange = () => { 449 | const rate = rates.value.find( 450 | (rate) => 451 | rate.fromCurrency === form.value.fromCurrency && 452 | rate.toCurrency === form.value.toCurrency 453 | )?.rate; 454 | 455 | if (rate) { 456 | result.value = form.value.amount * rate; 457 | } else { 458 | result.value = null; 459 | } 460 | }; 461 | 462 | onMounted(fetchCurrencies); 463 | ``` 464 | 465 | 实现新闻页: 466 | 467 | 组件: 我们需要一个卡片 468 | 469 | 案例: 470 | 471 | ```html 472 | 483 | ``` 484 | 485 | button 文本按钮配置: 486 | 487 | ```html 488 | 495 | ``` 496 | 497 | 简单实现: 498 | 499 | ```html 500 |
501 | 502 |

标题

503 |

预览

504 | 阅读更多 505 |
506 |
507 |
No data
508 | ``` 509 | 510 | 最终实现: 511 | 512 | ```vue 513 | 531 | 532 | 556 | ``` 557 | 558 | 然后我们需要具体地配置路由: 559 | 560 | ```ts 561 | import { useRouter, useRoute } from "vue-router"; 562 | const router = useRouter(); 563 | const route = useRoute(); 564 | ``` 565 | 566 | App.vue 阶段性过程 567 | 568 | ```ts 569 | import { ref, watch } from "vue"; 570 | import { useRouter, useRoute } from "vue-router"; 571 | 572 | const router = useRouter(); 573 | const route = useRoute(); 574 | 575 | const activeIndex = ref(route.name?.toString() || "home"); 576 | 577 | watch(route, (newRoute) => { 578 | activeIndex.value = newRoute.name?.toString() || "home"; 579 | }); 580 | 581 | const handleSelect = (key: string) => { 582 | router.push({ 583 | name: key.charAt(0).toUpperCase() + key.slice(1), 584 | }); 585 | }; 586 | ``` 587 | 588 | 实现登录/注册页面和对应功能: 589 | 590 | componennts/login.vue 591 | 592 | 参考表单: 593 | 594 | ```html 595 | 610 | ``` 611 | 612 | 注册就是把登录改成注册即可。 613 | 614 | 接下来我们修改路由就把之前的完整代码复制一下即可。 615 | 616 | 完善 App.vue 617 | 618 | ```html 619 | 登录 622 | 注册 625 | ``` 626 | 627 | 实现具体的注册逻辑: 628 | 629 | 完成代码: 630 | 631 | ```ts 632 | const register = async () => { 633 | const username: string = form.value.username; 634 | const password: string = form.value.password; 635 | 636 | // 发送注册请求 637 | const response = await axios.post("/auth/register", { username, password }); 638 | 639 | // 存储 token 640 | // 测试代码 641 | console.log(response); 642 | //data.token 643 | token.value = response.data.token; 644 | localStorage.setItem("token", token.value || ""); 645 | 646 | // 跳转到新闻页面 647 | router.push({ name: "News" }); 648 | }; 649 | ``` 650 | 651 | 登录页模板 652 | 653 | ```html 654 | 673 | ``` 674 | 675 | auth.ts 中注册部分 676 | 677 | ```ts 678 | const register = async (username: string, password: string) => { 679 | try { 680 | const response = await axios.post("/auth/register", { username, password }); 681 | token.value = response.data.token; 682 | localStorage.setItem("token", token.value || ""); 683 | } catch (error) { 684 | console.error("Register error:", error); 685 | } 686 | }; 687 | ``` 688 | 689 | 错误提示案例: 690 | 691 | ```ts 692 | ElMessage.error("Oops, this is a error message."); 693 | ``` 694 | 695 | 注册最终案例: 696 | 697 | ```ts 698 | const register = async () => { 699 | try { 700 | await authStore.register(form.value.username, form.value.password); 701 | router.push({ name: "News" }); 702 | } catch { 703 | ElMessage.error("注册失败,请重试。"); 704 | } 705 | }; 706 | ``` 707 | 708 | App.vue 导航栏最终案例: 709 | 710 | ```html 711 | 登录 714 | 注册 717 | 退出 720 | ``` 721 | 722 | 文章权限实现说明 723 | 724 | `authStore.isAuthenticated` => `false` 说明未登录 725 | 726 | `!authStore.isAuthenticated` => `true` 说明未登录, 所以会执行下面的错误提示。 727 | 728 | 具体代码 729 | 730 | ```ts 731 | const viewDetail = (id: string) => { 732 | if (!authStore.isAuthenticated) { 733 | ElMessage.error("请先登录后再查看"); 734 | return; 735 | } 736 | router.push({ name: "NewsDetail", params: { id } }); 737 | }; 738 | ``` 739 | 740 | 文章具体页面 741 | 742 | ```vue 743 | 754 | 755 | 781 | 782 | 793 | ``` 794 | 795 | ## 莫名其妙的报错 796 | 797 | 莫名其妙的报错指编辑器总是说找不到文件/对应的类型声明, 这个时候修改 tsconfig 就很重要了。然后重新启动(`npm run dev`)直接用下面的即可, 假如依然不行, 私信联系 UP 主即可, 因为还有几个对策, 但是没有写出来。 798 | 799 | vue文件的类型声明 shims-vue.d.ts 800 | 801 | ```ts 802 | declare module '*.vue' { 803 | import { DefineComponent } from 'vue'; 804 | const component: DefineComponent<{}, {}, any>; 805 | export default component; 806 | } 807 | ``` 808 | 809 | ```json 810 | { 811 | "compilerOptions": { 812 | "target": "esnext", 813 | "module": "esnext", 814 | "strict": true, 815 | "jsx": "preserve", 816 | "moduleResolution": "node", 817 | "resolveJsonModule": true, 818 | "esModuleInterop": true, 819 | "skipLibCheck": true, 820 | "forceConsistentCasingInFileNames": true, 821 | "baseUrl": ".", 822 | "paths": { 823 | "@/*": ["src/*"] 824 | }, 825 | "types": ["vite/client"] 826 | }, 827 | "include": ["src/**/*.ts", "src/**/*.d.ts", "src/**/*.tsx", "src/**/*.vue"] 828 | } 829 | ``` 830 | 831 | ## CSS 文件 832 | 833 | 为了节约 CSS 时间, 我们 CSS 文件都不会写。以下是每个文件的 CSS 834 | 835 | currencyExchangeView.vue 836 | 837 | ```css 838 | .exchange-form { 839 | width: 100%; 840 | max-width: 600px; 841 | margin: 20px auto; 842 | padding: 20px; 843 | background-color: #f5f5f5; 844 | border-radius: 4px; 845 | box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1); 846 | } 847 | 848 | .result { 849 | margin-top: 20px; 850 | padding: 20px; 851 | background-color: #f0f0f0; 852 | border-radius: 4px; 853 | text-align: center; 854 | font-size: 18px; 855 | } 856 | ``` 857 | 858 | Homeview.vue 859 | 860 | ```css 861 | .home-container { 862 | display: flex; 863 | justify-content: center; 864 | align-items: center; 865 | height: 100vh; 866 | background-color: #f5f5f5; 867 | padding: 20px; 868 | box-sizing: border-box; 869 | } 870 | 871 | .content-wrapper { 872 | text-align: center; 873 | max-width: 800px; 874 | } 875 | 876 | .title { 877 | color: #333; 878 | font-size: 36px; 879 | font-weight: bold; 880 | margin-bottom: 20px; 881 | } 882 | 883 | .description { 884 | color: #666; 885 | font-size: 18px; 886 | line-height: 1.5; 887 | } 888 | ``` 889 | 890 | newsView.css 891 | 892 | ```css 893 | .article-card { 894 | margin: 20px 0; 895 | } 896 | 897 | .no-data { 898 | text-align: center; 899 | font-size: 1.2em; 900 | color: #999; 901 | } 902 | ``` 903 | 904 | app.vue 905 | 906 | ```css 907 | .el-menu-demo { 908 | line-height: 60px; 909 | } 910 | ``` 911 | 912 | login.vue 913 | 914 | ```css 915 | .auth-container { 916 | display: flex; 917 | justify-content: center; 918 | align-items: center; 919 | height: 100vh; 920 | background-color: #f5f5f5; 921 | padding: 20px; 922 | box-sizing: border-box; 923 | } 924 | 925 | .auth-form { 926 | width: 100%; 927 | max-width: 360px; 928 | padding: 20px; 929 | background-color: #fff; 930 | border-radius: 4px; 931 | box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1); 932 | } 933 | ``` 934 | 935 | register.vue 936 | 937 | ```css 938 | .auth-container { 939 | display: flex; 940 | justify-content: center; 941 | align-items: center; 942 | height: 100vh; 943 | background-color: #f5f5f5; 944 | padding: 20px; 945 | box-sizing: border-box; 946 | } 947 | 948 | .auth-form { 949 | width: 100%; 950 | max-width: 360px; 951 | padding: 20px; 952 | background-color: #fff; 953 | border-radius: 4px; 954 | box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1); 955 | } 956 | ``` 957 | 958 | 补: 959 | newsDetailView.vue 960 | 961 | ```css 962 | .article-detail { 963 | margin: 20px 0; 964 | } 965 | 966 | .no-data { 967 | text-align: center; 968 | font-size: 1.2em; 969 | color: #999; 970 | } 971 | ``` 972 | 973 | ## 总结 974 | 975 | 以上就是全部内容, 如果有任何问题, 欢迎私信 UP 主反馈! 976 | 977 | 以上 祝学习成功! 978 | 979 | Inkka Plum 980 | -------------------------------------------------------------------------------- /后端配置说明.md: -------------------------------------------------------------------------------- 1 | # 后端配置说明 2 | 3 | 此说明为 B 站: InkkaPlum 频道的相关教程所用, 仅供学习。不得二次用于任何机构/个人再次录制 Vue/Node/Element 或其它任何语言, 框架, 架构, 工具等等教程中。但是非常欢迎修改项目或者添加更多功能。这个项目只是实现了基本的功能, 也有很多未完成的点。 4 | 此外, 下文有任何问题或者需要改进的点, 请联系 UP 主。 5 | 6 | ## 架构 7 | 8 | ```html 9 | server/ 10 | |-- src/ 11 | | |-- controllers/ 12 | | | |-- articleController.ts 13 | | | |-- authController.ts 14 | | | |-- exchangeRateController.ts 15 | | |-- middlewares/ 16 | | | |-- authMiddleware.ts 17 | | |-- models/ 18 | | | |-- Article.ts 19 | | | |-- ExchangeRate.ts 20 | | | |-- User.ts 21 | | |-- routes/ 22 | | | |-- article.ts 23 | | | |-- auth.ts 24 | | | |-- exchangeRate.ts 25 | | |-- app.ts 26 | | |-- index.ts 27 | |-- package.json 28 | |-- tsconfig.json 29 | ``` 30 | 31 | ## 配置说明 32 | 33 | 1. 配置相关依赖。 34 | 35 | 在 server 目录下:(不是 server 的话需要 cd server) 36 | 37 | ```bash 38 | npm i 39 | ``` 40 | 41 | 2. 本地安装对应程序 42 | 43 | a. [MongoDB](https://www.mongodb.com/) 44 | 45 | 下载[Community Edition](https://www.mongodb.com/try/download/community) 46 | 47 | 配置 GUI(图形用户界面) 48 | 49 | Products => Compass 50 | 51 | 或者直接点击[链接](https://www.mongodb.com/products/tools/compass) 52 | 53 | b. [Postman](https://www.postman.com/) 54 | 你需要注册一个账号 55 | 56 | 下载 Postman, [链接](https://www.postman.com/downloads/) 57 | 58 | ## 运行说明 59 | 60 | 1. 启动服务器, 需要查看 json 里面的内容。 61 | 62 | ```json 63 | "scripts": { 64 | "start": "ts-node src/index.ts", 65 | "dev": "ts-node-dev src/index.ts" 66 | }, 67 | ``` 68 | 69 | ```cmd 70 | npm run dev 71 | ``` 72 | 73 | 即可, 需要看到 74 | 75 | ```cmd 76 | Server is running on port 3000 77 | ``` 78 | 79 | 就说明成功了, 如果有 ERROR 消息, 请私信联系 UP 主。 80 | 81 | 2. 测试 API 82 | 83 | 点击加号即可 84 | ![Testing](image.png) 85 | 86 | 然后点击 GET/POST。 87 | 88 | 首先需要注册一个账号 89 | 90 | POST 91 | 92 | 地址: 93 | http://localhost:3000/api/auth/register 94 | 95 | 输入样式 96 | 97 | ```json 98 | { 99 | "username": "自己写名字", 100 | "password": "自己写密码" 101 | } 102 | ``` 103 | 104 | 返回的是你的 token: 105 | 106 | ```json 107 | { 108 | "token": "你的token" 109 | } 110 | ``` 111 | 112 | 把这个 token 复制下来, 点击 header, 键名叫 authorization, 键值即为 token 113 | 114 | ![authorization](image-1.png) 115 | 116 | 然后即可添加汇率, 和文章。 117 | 118 | a. 添加文章 119 | 120 | 地址: 用 POST 121 | http://localhost:3000/api/articles 122 | 123 | 内容: 124 | 125 | ```json 126 | { 127 | "title": "随意添加文本", 128 | "content": "文章内容", 129 | "preview": "文章预览内容", 130 | }, 131 | ``` 132 | 133 | b. 返回数据 134 | 135 | 地址: 用 GET 136 | http://localhost:3000/api/articles 137 | 138 | 案例: 139 | 140 | ```json 141 | [ 142 | { 143 | "_id": "114514", 144 | "title": "随意添加文本", 145 | "content": "文章内容", 146 | "preview": "文章预览内容", 147 | "createdBy": "114514", 148 | "__v": 0 149 | } 150 | ] 151 | ``` 152 | 153 | c. 添加汇率 154 | 155 | 地址: 用 POST 156 | http://localhost:3000/api/exchangeRates 157 | 158 | ```json 159 | { 160 | "fromCurrency": "USD", 161 | "toCurrency": "EUR", 162 | "rate": 1.1, 163 | }, 164 | ``` 165 | 166 | d. 返回数据 167 | 168 | 地址无异, 用GET即可。 169 | 170 | 案例: 171 | 172 | ```json 173 | [ 174 | { 175 | "_id": "114514", 176 | "fromCurrency": "USD", 177 | "toCurrency": "EUR", 178 | "rate": 1.1, 179 | "date": "1145-01-14T01:14:51.870Z", 180 | "__v": 0 181 | }, 182 | ] 183 | ``` 184 | 185 | e. 登录 186 | 187 | 用POST 188 | http://localhost:3000/api/auth/login 189 | 190 | ```json 191 | { 192 | "username": "自己的名字", 193 | "password": "自己的密码" 194 | } 195 | ``` 196 | 197 | 返回token: 198 | 199 | ```json 200 | { 201 | "token": "你的token" 202 | } 203 | ``` 204 | 205 | ## 修改说明 206 | 207 | 核心代码 208 | 209 | ```ts 210 | await mongoose.connect('mongodb://localhost:27017/currencyeg', { 211 | useNewUrlParser: true, 212 | useUnifiedTopology: true, 213 | } as ConnectOptions); 214 | console.log('Connected to MongoDB'); 215 | ``` 216 | 217 | `currencyeg`可以随便修改, 其它不要动即可。 218 | 219 | 点击Connect 220 | 221 | ![alt text](image-3.png) 222 | 223 | 然后既可以修改数据 224 | 225 | ![alt text](image-4.png) 226 | 227 | 代码请直接从GitHub上下载下来即可。 228 | 229 | 以上就是全部内容, 如果有任何问题, 欢迎私信UP主反馈! 230 | 231 | 以上 祝学习成功! 232 | 233 | Inkka Plum 234 | --------------------------------------------------------------------------------