├── .github └── workflows │ ├── build.yaml │ └── docker-build-push-release.yml ├── .gitignore ├── .prettierrc ├── Dockerfile ├── README.md ├── docker-compose.yml ├── example.env ├── nginx.conf ├── package.json ├── src ├── controllers │ ├── auth.ts │ ├── llm.ts │ └── nginx.ts ├── index.ts ├── static │ └── nginx-server-template.conf └── utils │ ├── auth.ts │ ├── general.ts │ └── nginx.ts ├── tsconfig.json └── yarn.lock /.github/workflows/build.yaml: -------------------------------------------------------------------------------- 1 | name: Build 2 | 3 | on: 4 | push: 5 | branches: 6 | - '*' 7 | - '!main' 8 | jobs: 9 | build: 10 | name: Build 11 | runs-on: ubuntu-latest 12 | steps: 13 | - name: Check out code 14 | uses: actions/checkout@v4 15 | 16 | - name: Build docker image 17 | run: | 18 | docker build . -------------------------------------------------------------------------------- /.github/workflows/docker-build-push-release.yml: -------------------------------------------------------------------------------- 1 | name: Docker Build, Push, and Release 2 | 3 | on: 4 | push: 5 | branches: 6 | - main 7 | 8 | env: 9 | REGISTRY: ghcr.io 10 | IMAGE_NAME: ${{ github.repository }} 11 | KEEP_RELEASES: 10 12 | KEEP_IMAGES: 10 13 | 14 | jobs: 15 | build-push-release: 16 | runs-on: ubuntu-latest 17 | permissions: 18 | contents: write 19 | packages: write 20 | pull-requests: read 21 | 22 | steps: 23 | - name: Checkout repository 24 | uses: actions/checkout@v4 25 | with: 26 | fetch-depth: 0 27 | 28 | - name: Get version from package.json 29 | id: package-version 30 | run: echo "VERSION=$(node -p "require('./package.json').version")" >> $GITHUB_OUTPUT 31 | 32 | - name: Set up Docker Buildx 33 | uses: docker/setup-buildx-action@v3 34 | 35 | - name: Cache Docker layers 36 | uses: actions/cache@v3 37 | with: 38 | path: /tmp/.buildx-cache 39 | key: ${{ runner.os }}-buildx-${{ github.sha }} 40 | restore-keys: | 41 | ${{ runner.os }}-buildx- 42 | 43 | - name: Cache npm dependencies 44 | uses: actions/cache@v3 45 | with: 46 | path: ~/.npm 47 | key: ${{ runner.os }}-node-${{ hashFiles('**/package-lock.json') }} 48 | restore-keys: | 49 | ${{ runner.os }}-node- 50 | 51 | - name: Log in to the Container registry 52 | uses: docker/login-action@v3 53 | with: 54 | registry: ${{ env.REGISTRY }} 55 | username: ${{ github.actor }} 56 | password: ${{ secrets.GITHUB_TOKEN }} 57 | 58 | - name: Extract metadata (tags, labels) for Docker 59 | id: meta 60 | uses: docker/metadata-action@v5 61 | with: 62 | images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }} 63 | tags: | 64 | type=raw,value=${{ steps.package-version.outputs.VERSION }} 65 | 66 | - name: Build and push Docker image 67 | uses: docker/build-push-action@v5 68 | with: 69 | context: . 70 | push: true 71 | tags: ${{ steps.meta.outputs.tags }} 72 | labels: ${{ steps.meta.outputs.labels }} 73 | cache-from: type=local,src=/tmp/.buildx-cache 74 | cache-to: type=local,dest=/tmp/.buildx-cache-new,mode=max 75 | 76 | - name: Move cache 77 | run: | 78 | rm -rf /tmp/.buildx-cache 79 | mv /tmp/.buildx-cache-new /tmp/.buildx-cache 80 | 81 | # - name: Scan Docker image for vulnerabilities 82 | # uses: aquasecurity/trivy-action@master 83 | # env: 84 | # TRIVY_USERNAME: ${{ github.actor }} 85 | # TRIVY_PASSWORD: ${{ secrets.GITHUB_TOKEN }} 86 | # with: 87 | # image-ref: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ steps.package-version.outputs.VERSION }} 88 | # format: 'table' 89 | # exit-code: '1' 90 | # ignore-unfixed: true 91 | # vuln-type: 'os,library' 92 | # severity: 'CRITICAL,HIGH' 93 | 94 | - name: Get Pull Request Messages 95 | id: pr-messages 96 | run: | 97 | PR_MESSAGES=$(git log --merges --format="%b" @~1..HEAD | sed 's/^/* /') 98 | echo "PR_MESSAGES<> $GITHUB_OUTPUT 99 | echo "$PR_MESSAGES" >> $GITHUB_OUTPUT 100 | echo "EOF" >> $GITHUB_OUTPUT 101 | 102 | - name: Create Release 103 | id: create_release 104 | uses: actions/create-release@v1 105 | env: 106 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 107 | with: 108 | tag_name: v${{ steps.package-version.outputs.VERSION }} 109 | release_name: Release v${{ steps.package-version.outputs.VERSION }} 110 | body: | 111 | ## Docker Image 112 | Image: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ steps.package-version.outputs.VERSION }} 113 | 114 | ## Pull Request Messages 115 | ${{ steps.pr-messages.outputs.PR_MESSAGES }} 116 | 117 | ## Security Scan 118 | A security scan was performed on this Docker image. Any critical or high vulnerabilities would have prevented this release. 119 | draft: false 120 | prerelease: false 121 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | node_modules 3 | .env 4 | dist -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "printWidth": 120, 3 | "singleQuote": true, 4 | "tabWidth": 2, 5 | "trailingComma": "none", 6 | "semi": false 7 | } 8 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | # Use an official Node.js runtime as the base image 2 | FROM node:18.20.3-bullseye 3 | 4 | # Install Nginx 5 | RUN apt-get update && apt-get install -y nginx certbot python3-certbot-dns-cloudflare 6 | 7 | # Create directory for Nginx configuration files 8 | RUN mkdir -p /etc/nginx/conf.d/ 9 | 10 | # Create directory for Let's Encrypt certificates 11 | RUN mkdir -p /etc/letsencrypt/ 12 | 13 | # Make sure Nginx has the right permissions to run 14 | RUN chown -R www-data:www-data /var/lib/nginx 15 | 16 | # Create app directory 17 | WORKDIR /app 18 | 19 | # Bundle app source 20 | COPY . . 21 | 22 | # Install dependencies & build 23 | RUN yarn && yarn build 24 | 25 | # Expose port 8080 & 443 26 | EXPOSE 8080 443 27 | 28 | # Remove the default Nginx configuration file 29 | RUN rm /etc/nginx/sites-enabled/default 30 | 31 | # Copy a custom Nginx configuration file 32 | COPY nginx.conf /etc/nginx/nginx.conf 33 | 34 | 35 | 36 | 37 | # Start Node.js app 38 | CMD ["node", "dist/index.js"] -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # LLM Proxy 2 | 3 | Manages Nginx for reverse proxy to multiple LLMs, with TLS & Bearer Auth tokens. Deployed with docker. 4 | 5 | - Aggregates multiple OpenAI-type LLM APIs 6 | - Supports cloudflare domains 7 | - Uses Let's Encrypt for TLS certificates 8 | - Uses certbot for certificate issuance and renewal 9 | - Uses Nginx as a public-domain reverse proxy to add TLS 10 | - Uses JWT for bearer authentication 11 | - Auth & nginx enpoints are IP restricted. 12 | 13 | ***All requests to `/v1/*` are proxied to the LLM APIs except for `/v1/models`*** 14 | 15 | `/v1/models` is a special endpoint that returns the list of models available from all LLM APIs. 16 | 17 | ## How to use 18 | 19 | Docker compose is going to be the easiest way to get up and running, but you could also manually run the docker image. Before you do anything else, if you don't have a cloudflare account, sign up now - it's free. You will need to create an API Token with the "Zone", "DNS", "Edit" permissions. This will be used when issuing your certs to verify that you own the domain you want to use for TLS. After you have your key, set up a new DNS record to point to your IP address. This can be proxied on the cloudflare end. 20 | 21 | After this, you'll set up your files, start the container, hit a few routes and you'll be good to go! 22 | 23 | #### **Don't forget to forward port 443 on your router!** 24 | 25 | I'll use `localhost`, `192.168.1.100` or `your.domain.com` as an examples, but fill these in with your domain or IP address. 26 | You can also add api keys if you need to. like `http://192.168.1.100:1234|api-key-here`. 27 | 28 | ### Files 29 | 30 | Here's what you'll need in your docker-compose file: 31 | ```yaml 32 | version: '3.6' 33 | 34 | services: 35 | llmp: 36 | image: ghcr.io/j4ys0n/llm-proxy:1.5.4 37 | container_name: llmp 38 | hostname: llmp 39 | restart: unless-stopped 40 | ports: 41 | - 8080:8080 42 | - 443:443 43 | volumes: 44 | - .env:/app/.env # environment variables 45 | - ./data:/app/data # any data that the app needs to persist 46 | - ./cloudflare_credentials:/opt/cloudflare/credentials # cloudflare api token 47 | - ./nginx:/etc/nginx/conf.d # nginx configs 48 | - ./certs:/etc/letsencrypt # tsl certificates 49 | ``` 50 | 51 | Here's what your `.env` file should look like: 52 | ```bash 53 | PORT=8080 # node.js listen port. right now nginx is hard coded, so don't change this. 54 | TARGET_URLS=http://localhost:1234,http://192.168.1.100:1234|api-key-here # list of api endpoints (/v1 is optional) 55 | JWT_SECRET=randomly_generated_secret # secret for JWT token generation, change this! 56 | AUTH_USERNAME=admin 57 | AUTH_PASSWORD=secure_password # super basic auth credentials for the admin interface 58 | ``` 59 | 60 | Here's what your cloudflare_credendials file should look like 61 | ```bash 62 | dns_cloudflare_api_token = your_token_here 63 | ``` 64 | 65 | ### Routes 66 | 67 | You'll need to use the local, unsecured endpoints to get set up initially. The `/auth/token` endpoint is the only endpoint that does't need an Authorization header and token. 68 | 69 | Generate tokens. 70 | 71 | `POST http://192.168.1.100:8080/auth/token` 72 | ```json 73 | { 74 | "username": "admin", 75 | "password": "secure_password" 76 | } 77 | ``` 78 | response: 79 | ```json 80 | { 81 | "token": "generated_token_here" 82 | } 83 | ``` 84 | 85 | #### All of the routes below need a bearer token in the Authorization header. 86 | `Authorization: Bearer generated_token_here` 87 | 88 | Get TLS certificates. 89 | 90 | `POST http://192.168.1.100:8080/nginx/certificates/obtain` 91 | ```json 92 | { 93 | "domains": ["your.domain.com"] 94 | } 95 | ``` 96 | response: 97 | ```json 98 | { 99 | "success": true, 100 | "message": "Certificates obtained successfully." 101 | } 102 | ``` 103 | 104 | Write default config with your domain. (this should be sufficient for you, fill in your domain and cider groups) 105 | 106 | Note: you can add multiple CIDR groups if you have multiple internal IP ranges you want admin functions to be accessible to. This is all of the routes that start with `/auth` or `/nginx`. 107 | 108 | Hint: `192.168.1.0/24` will allow all IPs from `192.168.1.1` - `192.168.1.254`. `192.168.1.111/32` will only allow `192.168.1.111`. 109 | 110 | `POST http://192.168.1.100:8080/nginx/config/write-default` 111 | ```json 112 | { 113 | "domain": "your.domain.com", 114 | "cidrGroups": ["192.168.1.0/24"] 115 | } 116 | ``` 117 | response: 118 | ```json 119 | { 120 | "success": true, 121 | "message": "Default config written successfully" 122 | } 123 | ``` 124 | 125 | Reload nginx to apply changes. 126 | `GET http://192.168.1.100:8080/nginx/reload` 127 | response: 128 | ```json 129 | { 130 | "success": true, 131 | "message": "Nginx configuration reloaded successfully." 132 | } 133 | ``` 134 | 135 | ***If you made it here, you should be good to go!*** 136 | 137 | Other available endpoints (these will be documented better in the future) 138 | 139 | `GET /nginx/config/get` - get current nginx config as a string. 140 | 141 | `POST /nginx/config/update` - update the nginx config with a custom domain. 142 | Body: `{ "config": string }` 143 | 144 | `GET /nginx/config/get-default` - get default nginx config template. 145 | 146 | `GET /nginx/certificates/renew` - renew certificates for your domains. -------------------------------------------------------------------------------- /docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: '3.6' 2 | 3 | services: 4 | llmp: 5 | image: ghcr.io/j4ys0n/llm-proxy:1.5.4 6 | container_name: llmp 7 | hostname: llmp 8 | restart: unless-stopped 9 | ports: 10 | - 8080:8080 11 | - 443:443 12 | volumes: 13 | - .env:/app/.env 14 | - ./data:/app/data 15 | - ./cloudflare_credentials:/opt/cloudflare/credentials 16 | - ./nginx:/etc/nginx/conf.d # nginx configs 17 | - ./certs:/etc/letsencrypt # tsl certificates 18 | logging: 19 | driver: 'json-file' 20 | options: 21 | max-size: 100m 22 | max-file: '2' -------------------------------------------------------------------------------- /example.env: -------------------------------------------------------------------------------- 1 | PORT=8080 2 | TARGET_URLS=http://localhost:1234/v1,http://192.168.1.100:1234|api-key-here 3 | JWT_SECRET=your-jwt-secret-key-here 4 | AUTH_USERNAME=admin 5 | AUTH_PASSWORD=secure_password -------------------------------------------------------------------------------- /nginx.conf: -------------------------------------------------------------------------------- 1 | user www-data; 2 | worker_processes auto; 3 | pid /run/nginx.pid; 4 | include /etc/nginx/modules-enabled/*.conf; 5 | 6 | events { 7 | worker_connections 1024; 8 | } 9 | 10 | http { 11 | sendfile on; 12 | # tcp_nopush on; 13 | types_hash_max_size 2048; 14 | include /etc/nginx/mime.types; 15 | default_type application/octet-stream; 16 | 17 | ssl_protocols TLSv1 TLSv1.1 TLSv1.2 TLSv1.3; 18 | ssl_prefer_server_ciphers on; 19 | 20 | keepalive_timeout 65; 21 | 22 | access_log /var/log/nginx/access.log; 23 | error_log /var/log/nginx/error.log; 24 | 25 | gzip on; 26 | 27 | include /etc/nginx/conf.d/*.conf; 28 | # server { 29 | # listen 80; 30 | # server_name localhost; 31 | 32 | # location / { 33 | # proxy_pass http://localhost:3000; 34 | # proxy_http_version 1.1; 35 | # proxy_set_header Upgrade $http_upgrade; 36 | # proxy_set_header Connection 'upgrade'; 37 | # proxy_set_header Host $host; 38 | # proxy_cache_bypass $http_upgrade; 39 | # } 40 | # } 41 | } -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "llm-proxy", 3 | "version": "1.5.4", 4 | "description": "Manages Nginx for reverse proxy to multiple LLMs, with TLS & Bearer Auth tokens", 5 | "main": "dist/index.js", 6 | "scripts": { 7 | "start": "node dist/index.js", 8 | "build": "rm -rf dist && tsc && cp -r src/static dist/", 9 | "dev": "ts-node-dev --respawn --transpile-only src/index.ts", 10 | "test": "echo \"Error: no test specified\" && exit 1" 11 | }, 12 | "keywords": [ 13 | "node", 14 | "nginx", 15 | "typescript", 16 | "reverse proxy", 17 | "llm", 18 | "openai", 19 | "certificate", 20 | "bearer auth", 21 | "tls", 22 | "ai" 23 | ], 24 | "author": "Jayson Jacobs", 25 | "license": "Apache-2.0", 26 | "dependencies": { 27 | "axios": "^1.7.2", 28 | "dotenv": "^10.0.0", 29 | "express": "^4.19.2", 30 | "fs-extra": "^11.2.0", 31 | "jsonwebtoken": "^9.0.2" 32 | }, 33 | "devDependencies": { 34 | "@types/express": "^4.17.21", 35 | "@types/fs-extra": "^11.0.4", 36 | "@types/jsonwebtoken": "^9.0.2", 37 | "@types/node": "^20.12.7", 38 | "ts-node-dev": "^2.0.0", 39 | "typescript": "^5.4.5" 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/controllers/auth.ts: -------------------------------------------------------------------------------- 1 | import { Express, Request, Response } from 'express' 2 | import jwt from 'jsonwebtoken' 3 | import { log } from '../utils/general' 4 | 5 | const jwtSecret = process.env.JWT_SECRET || 'your-jwt-secret' 6 | const authUsername = process.env.AUTH_USERNAME || 'admin' 7 | const authPassword = process.env.AUTH_PASSWORD || 'secure_password' 8 | 9 | export class AuthController { 10 | private app: Express 11 | 12 | constructor({ app }: { app: Express }) { 13 | this.app = app 14 | } 15 | 16 | public registerRoutes(): void { 17 | this.app.post('/auth/token', this.getToken.bind(this)) 18 | log('info', 'AuthController initialized') 19 | } 20 | 21 | private getToken(req: Request, res: Response): void { 22 | const { username, password } = req.body 23 | if (username === authUsername && password === authPassword) { 24 | const token = jwt.sign({ username }, jwtSecret, { algorithm: 'HS256' }) 25 | log('info', `token generated for ${username}`) 26 | res.json({ token }) 27 | } else { 28 | res.status(401).json({ error: 'Invalid credentials' }) 29 | } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/controllers/llm.ts: -------------------------------------------------------------------------------- 1 | import { Express, NextFunction, Request, RequestHandler, Response } from 'express' 2 | import { log, md5, sleep, extractDomainName } from '../utils/general' 3 | import axios, { AxiosRequestConfig } from 'axios' 4 | 5 | export interface Model { 6 | id: string 7 | object: string 8 | owned_by: string 9 | permission: Array 10 | } 11 | 12 | export interface ModelMap { 13 | [key: string]: { url: string; model: Model } 14 | } 15 | 16 | const defaultContentType = 'application/json' 17 | 18 | function getPath(url: string): { path: string, base: string, apiKey?: string } { 19 | try { 20 | const urlParts = url.split('|') 21 | const apiKey = urlParts.length > 1 ? urlParts[1] : undefined 22 | const { origin, pathname } = new URL(urlParts[0]) 23 | return { 24 | path: pathname === '/' ? '/v1' : pathname, 25 | base: origin, 26 | apiKey 27 | } 28 | } catch (error) { 29 | // Return the input if it's already a path starting with '/' 30 | if (url.startsWith('/')) return { path: url, base: 'http://localhost' } 31 | // Return '/v1' for invalid URLs 32 | return { path: '/v1', base: 'http://localhost' } 33 | } 34 | } 35 | 36 | async function fetchModels(targetUrls: string[]): Promise { 37 | const tmp: ModelMap = {} 38 | for (const urlAndToken of targetUrls) { 39 | const { path, base, apiKey } = getPath(urlAndToken) 40 | const headers: { [key: string]: string } = { 41 | accept: defaultContentType, 42 | 'Content-Type': defaultContentType 43 | } 44 | if (apiKey != null && apiKey !== '') { 45 | headers['Authorization'] = `Bearer ${apiKey}` 46 | } 47 | const params = { 48 | method: 'GET', 49 | url: `${base}${path}/models`, 50 | headers 51 | } 52 | try { 53 | const response = await axios(params) 54 | const models = response.data.data || [] 55 | const hostId = extractDomainName(base) 56 | models.forEach((model: Model) => { 57 | const hash = md5(model.id) 58 | tmp[hash] = { url: urlAndToken, model } 59 | }) 60 | log('info', `Models cached successfully for ${base}. [${models.map((m: Model) => m.id).join(', ')}]`) 61 | } catch (error) { 62 | log('error', `Error fetching models from ${base}${path}/models: ${(error as any).toString()}`) 63 | } 64 | } 65 | return tmp 66 | } 67 | 68 | export class LLMController { 69 | private app: Express 70 | private requestHandlers: RequestHandler[] 71 | private targetUrls: Array = [] 72 | private modelCache: ModelMap = {} 73 | 74 | constructor({ 75 | app, 76 | requestHandlers, 77 | targetUrls 78 | }: { 79 | app: Express 80 | requestHandlers: RequestHandler[] 81 | targetUrls: string[] 82 | }) { 83 | this.app = app 84 | this.requestHandlers = requestHandlers 85 | this.targetUrls = targetUrls 86 | } 87 | 88 | public registerRoutes(): void { 89 | this.app.get('/v1/models', ...this.requestHandlers, this.models.bind(this)) 90 | this.app.use('/', ...this.requestHandlers, this.forwardPostRequest.bind(this)) 91 | log('info', 'LLMController routes registered') 92 | log('info', 'fetching model lists') 93 | this.cacheModels() 94 | } 95 | 96 | private async cacheModels() { 97 | while (true) { 98 | this.modelCache = await fetchModels(this.targetUrls) 99 | await sleep(60000) 100 | } 101 | } 102 | 103 | private models(req: Request, res: Response): void { 104 | const combinedModels = Object.values(this.modelCache).map((item) => item.model) 105 | res.json({ data: combinedModels, object: 'list' }) 106 | } 107 | 108 | public async forwardPostRequest(req: Request, res: Response, next: NextFunction) { 109 | if ( 110 | req.method === 'POST' && 111 | (req.path.startsWith('v1') || req.path.startsWith('/v1')) && 112 | req.body != null && 113 | req.body.model != null && 114 | this.targetUrls.length > 0 115 | ) { 116 | const { model: modelId } = req.body 117 | const { base: firstBaseUrl, path: firstPath, apiKey: firstApiKey } = getPath(this.targetUrls[0]) 118 | let targetUrl = firstBaseUrl // Default to first URL if no matching model found 119 | let targetPath = firstPath 120 | let targetApiKey = firstApiKey 121 | 122 | const hash = md5(modelId) 123 | if (modelId && this.modelCache[hash]) { 124 | const { path, base, apiKey } = getPath(this.modelCache[hash].url) 125 | targetUrl = base 126 | targetPath = path 127 | targetApiKey = apiKey 128 | } 129 | const reqPath = req.path.startsWith('/v1/') ? req.path.replace('/v1', targetPath) : `${targetPath}${req.path}` 130 | const fullUrl = new URL(reqPath, targetUrl).toString() 131 | log('info', `Forwarding request to: ${fullUrl} -> ${modelId}`) 132 | const headers = { ...req.headers } 133 | if (targetApiKey) { 134 | headers['Authorization'] = `Bearer ${targetApiKey}` 135 | } 136 | try { 137 | const axiosConfig: AxiosRequestConfig = { 138 | method: req.method, 139 | url: fullUrl, 140 | headers, 141 | data: req.body, 142 | responseType: 'stream' 143 | } 144 | 145 | // Remove headers that might cause issues 146 | if (axiosConfig.headers != null) { 147 | delete axiosConfig.headers['host'] 148 | delete axiosConfig.headers['content-length'] 149 | } 150 | 151 | const axiosResponse = await axios(axiosConfig) 152 | 153 | // Forward the response status and headers 154 | res.status(axiosResponse.status) 155 | Object.entries(axiosResponse.headers).forEach(([key, value]) => { 156 | res.setHeader(key, value) 157 | }) 158 | 159 | // Pipe the response data 160 | axiosResponse.data.pipe(res) 161 | } catch (error) { 162 | log(`Error forwarding request: ${(error as any).toString()}`, 'error') 163 | if (axios.isAxiosError(error) && error.response) { 164 | // log(`Error response from ${fullUrl}:`, 'error', error.response.data) 165 | log(`Request body caused error:`, 'error', req.body) 166 | res.status(error.response.status).json({ error: 'Error processing request' }) 167 | } else { 168 | res.status(500).json({ error: 'Internal Server Error' }) 169 | } 170 | } 171 | } else { 172 | next() 173 | } 174 | } 175 | } 176 | -------------------------------------------------------------------------------- /src/controllers/nginx.ts: -------------------------------------------------------------------------------- 1 | import { Express, Request, Response, RequestHandler } from 'express' 2 | import { NginxManager } from '../utils/nginx' 3 | import { log } from '../utils/general' 4 | 5 | export class NginxController { 6 | private app: Express 7 | private nginxManager = new NginxManager() 8 | private requestHandlers: RequestHandler[] 9 | 10 | constructor({ app, requestHandlers }: { app: Express; requestHandlers: RequestHandler[] }) { 11 | this.app = app 12 | this.requestHandlers = requestHandlers 13 | } 14 | 15 | public registerRoutes(): void { 16 | this.app.get('/nginx/reload', ...this.requestHandlers, this.reloadNginx.bind(this)) 17 | this.app.post('/nginx/config/update', ...this.requestHandlers, this.updateConfig.bind(this)) 18 | this.app.get('/nginx/config/get', ...this.requestHandlers, this.getConfig.bind(this)) 19 | this.app.get('/nginx/config/get-default', ...this.requestHandlers, this.getDefaultConfig.bind(this)) 20 | this.app.post('/nginx/config/write-default', ...this.requestHandlers, this.writeDefaultConfig.bind(this)) 21 | this.app.post('/nginx/certificates/obtain', ...this.requestHandlers, this.obtainCertificates.bind(this)) 22 | this.app.get('/nginx/certificates/renew', ...this.requestHandlers, this.renewCertificates.bind(this)) 23 | log('info', 'NginxController initialized') 24 | } 25 | 26 | public async start(): Promise { 27 | const { success, message } = await this.nginxManager.start() 28 | } 29 | 30 | private async reloadNginx(req: Request, res: Response): Promise { 31 | const { success, message } = await this.nginxManager.reload() 32 | const status = success ? 200 : 500 33 | res.status(status).json({ success, message }) 34 | } 35 | 36 | private async updateConfig(req: Request, res: Response): Promise { 37 | if (req.body != null && req.body.config != null) { 38 | const newConfig = req.body.config 39 | const { success, message } = await this.nginxManager.updateConfig(newConfig) 40 | const status = success ? 200 : 500 41 | res.status(status).json({ success, message }) 42 | } else { 43 | res.status(400).json({ success: false, message: 'Invalid request body' }) 44 | } 45 | } 46 | 47 | private async getConfig(req: Request, res: Response): Promise { 48 | const { success, message, config } = await this.nginxManager.getConfig() 49 | const status = success ? 200 : 500 50 | res.status(status).json({ success, message, config }) 51 | } 52 | 53 | private async getDefaultConfig(req: Request, res: Response): Promise { 54 | const { success, message, config } = await this.nginxManager.getTemplate() 55 | if (success && config) { 56 | res.json({ success, config }) 57 | } else { 58 | res.status(500).json({ success, message }) 59 | } 60 | } 61 | 62 | private async writeDefaultConfig(req: Request, res: Response): Promise { 63 | if (req.body != null && req.body.domain != null && req.body.cidrGroups != null) { 64 | const { domain, cidrGroups } = req.body 65 | if (Array.isArray(cidrGroups) && typeof domain === 'string') { 66 | const { success, message } = await this.nginxManager.writeDefaultTemplate(domain, cidrGroups) 67 | if (success) { 68 | res.json({ success, message: 'Default config written successfully' }) 69 | } else { 70 | res.status(500).json({ success, message }) 71 | } 72 | } else { 73 | res.status(400).json({ success: false, message: 'Invalid request body' }) 74 | } 75 | } else { 76 | res.status(400).json({ success: false, message: 'Invalid request body' }) 77 | } 78 | } 79 | 80 | private async obtainCertificates(req: Request, res: Response): Promise { 81 | if (req.body != null && req.body.domains != null && Array.isArray(req.body.domains)) { 82 | const domains = req.body.domains 83 | const { success, message } = await this.nginxManager.obtainCertificates(domains, true) 84 | const status = success ? 200 : 500 85 | res.status(status).json({ success, message }) 86 | } else { 87 | res.status(400).json({ success: false, message: 'Invalid request body' }) 88 | } 89 | } 90 | 91 | private async renewCertificates(req: Request, res: Response): Promise { 92 | const { success, message } = await this.nginxManager.renewCertificates() 93 | const status = success ? 200 : 500 94 | res.status(status).json({ success, message }) 95 | } 96 | } -------------------------------------------------------------------------------- /src/index.ts: -------------------------------------------------------------------------------- 1 | import dotenv from 'dotenv' 2 | import express from 'express' 3 | import { NginxController } from './controllers/nginx' 4 | import { LLMController } from './controllers/llm' 5 | import { tokenMiddleware } from './utils/auth' 6 | import { AuthController } from './controllers/auth' 7 | import { log } from './utils/general' 8 | import bodyParser from 'body-parser' 9 | 10 | dotenv.config() 11 | 12 | const app = express() 13 | const port = process.env.PORT || 8080 14 | const targetUrls = (process.env.TARGET_URLS || 'http://example.com').split(',').map((url) => url.trim()) 15 | 16 | // app.use(express.json()) 17 | const payloadLimit = process.env.PAYLOAD_LIMIT || '1mb' 18 | app.use(bodyParser.json({ limit: payloadLimit })) 19 | app.use(bodyParser.urlencoded({ extended: false, limit: payloadLimit })) 20 | log('info', `Payload limit is: ${payloadLimit}`) 21 | 22 | // Express routes 23 | app.get('/', (req, res) => { 24 | res.send('LLM Proxy') 25 | }) 26 | 27 | const authController = new AuthController({ app }) 28 | authController.registerRoutes() 29 | 30 | const nginxController = new NginxController({ app, requestHandlers: [ tokenMiddleware ] }) 31 | nginxController.registerRoutes() 32 | nginxController.start() 33 | 34 | const llmController = new LLMController({ app, requestHandlers: [tokenMiddleware ], targetUrls }) 35 | llmController.registerRoutes() 36 | 37 | // Start the server 38 | app.listen(port, () => { 39 | console.log(`Local server running at http://localhost:${port}`) 40 | }) 41 | 42 | -------------------------------------------------------------------------------- /src/static/nginx-server-template.conf: -------------------------------------------------------------------------------- 1 | server { 2 | listen 443 ssl; 3 | 4 | add_header 'Access-Control-Allow-Origin' '*'; 5 | add_header 'Add-Control-Allow-Methods' 'GET, POST'; 6 | 7 | server_name {{domainName}}; 8 | server_tokens off; 9 | 10 | real_ip_header X-Forwarded-For; 11 | 12 | client_max_body_size 50M; 13 | 14 | ssl_certificate /etc/letsencrypt/live/{{domainName}}/fullchain.pem; 15 | ssl_certificate_key /etc/letsencrypt/live/{{domainName}}/privkey.pem; 16 | # include /etc/letsencrypt/options-ssl-nginx.conf; 17 | # ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem; 18 | 19 | location ~ ^/v1/(.*) { 20 | proxy_pass_header Authorization; 21 | proxy_pass http://localhost:8080; 22 | proxy_redirect off; 23 | proxy_set_header X-Real-IP $remote_addr; 24 | proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; 25 | proxy_http_version 1.1; 26 | proxy_set_header Upgrade $http_upgrade; 27 | proxy_set_header Connection 'upgrade'; 28 | proxy_set_header Host $host; 29 | proxy_cache_bypass $http_upgrade; 30 | proxy_buffering off; 31 | client_max_body_size 0; 32 | proxy_read_timeout 36000s; 33 | } 34 | 35 | location ~ /(auth|nginx)/(.*) { 36 | proxy_pass_header Authorization; 37 | proxy_pass http://localhost:8080; 38 | proxy_redirect off; 39 | proxy_set_header X-Real-IP $remote_addr; 40 | proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; 41 | proxy_http_version 1.1; 42 | proxy_set_header Upgrade $http_upgrade; 43 | proxy_set_header Connection 'upgrade'; 44 | proxy_set_header Host $host; 45 | proxy_cache_bypass $http_upgrade; 46 | proxy_buffering off; 47 | client_max_body_size 0; 48 | proxy_read_timeout 36000s; 49 | {{allowedIPs}} deny all; 50 | } 51 | 52 | location ~ (.*) { 53 | return 404; 54 | } 55 | } 56 | 57 | server { 58 | listen 443 ssl; 59 | 60 | server_name ${public_ip}; 61 | server_tokens off; 62 | return 444; # "Connection closed without response" 63 | } -------------------------------------------------------------------------------- /src/utils/auth.ts: -------------------------------------------------------------------------------- 1 | import { NextFunction, Request, Response, RequestHandler } from 'express' 2 | import jwt from 'jsonwebtoken' 3 | import { log } from './general' 4 | 5 | 6 | const jwtSecret = process.env.JWT_SECRET || 'your-jwt-secret' 7 | 8 | 9 | export const tokenMiddleware: RequestHandler = (req: Request, res: Response, next: NextFunction) => { 10 | const authHeader = req.headers.authorization 11 | if (authHeader && authHeader.startsWith('Bearer ')) { 12 | const token = authHeader.split(' ')[1] 13 | try { 14 | const decoded = jwt.verify(token, jwtSecret, { 15 | algorithms: ['HS256'], 16 | ignoreExpiration: true 17 | }) 18 | ;(req as any).user = decoded 19 | next() 20 | } catch (error) { 21 | log('error', `Token verification failed: ${(error as any).toString()}`) 22 | res.status(401).json({ error: 'Invalid token' }) 23 | } 24 | } else { 25 | log('warn', 'Authorization header missing or invalid') 26 | res.status(401).json({ error: 'Missing or invalid Authorization header' }) 27 | } 28 | } -------------------------------------------------------------------------------- /src/utils/general.ts: -------------------------------------------------------------------------------- 1 | import crypto from 'crypto' 2 | 3 | export function log(level: string, msg: string, error?: any) { 4 | console.log(`[${new Date().toISOString()}] [${level.toUpperCase()}] ${msg}`) 5 | if (error != null) { 6 | console.error(error) 7 | } 8 | } 9 | 10 | export function md5(data: Object | string): string { 11 | const stringValue = typeof data === 'string' ? data : JSON.stringify(data) 12 | return crypto.createHash('md5').update(stringValue).digest('hex') 13 | } 14 | 15 | export function sleep(ms: number) { 16 | return new Promise((r) => setTimeout(r, ms)) 17 | } 18 | 19 | export function extractDomainName(url: string) { 20 | return url.replace('https://', '').replace('http://', '').split('/')[0].replace(/\./g, '_') 21 | } -------------------------------------------------------------------------------- /src/utils/nginx.ts: -------------------------------------------------------------------------------- 1 | import { exec } from 'child_process' 2 | import { readFile, writeFile } from 'fs-extra' 3 | import { join } from 'path' 4 | import { promisify } from 'util' 5 | import { log } from './general' 6 | 7 | const execAsync = promisify(exec) 8 | 9 | export interface NginxResponse { 10 | success: boolean 11 | message?: string 12 | } 13 | 14 | export interface NginxConfigResponse { 15 | success: boolean 16 | config: string 17 | message?: string 18 | } 19 | 20 | const CONFIG_TEMPLATE_PATH = join(__dirname, '../static/nginx-server-template.conf') 21 | 22 | export class NginxManager { 23 | private configPath: string 24 | // private domains: string[] 25 | 26 | constructor(configPath: string = '/etc/nginx/conf.d/server.conf', domains: string[] = []) { 27 | this.configPath = configPath 28 | // this.domains = domains 29 | } 30 | 31 | async start(): Promise { 32 | let message: string | undefined 33 | let success = false 34 | try { 35 | await execAsync('nginx') 36 | message = 'Nginx started successfully.' 37 | success = true 38 | log('info', message) 39 | } catch (error) { 40 | log('error', 'Failed to start Nginx:', error) 41 | message = error != null ? (error as any).message ?? 'Error' : 'Unknown error' 42 | } 43 | return { success, message } 44 | } 45 | 46 | async reload(): Promise { 47 | let message: string | undefined 48 | let success = false 49 | try { 50 | await execAsync('nginx -s reload') 51 | message = 'Nginx configuration reloaded successfully.' 52 | success = true 53 | log('info', message) 54 | } catch (error) { 55 | log('error', 'Failed to reload Nginx.', error) 56 | message = error != null ? (error as any).message ?? 'Error' : 'Unknown error' 57 | } 58 | return { success, message } 59 | } 60 | 61 | async updateConfig(newConfig: string): Promise { 62 | return this.putFile(this.configPath, newConfig) 63 | } 64 | 65 | async writeDefaultTemplate(domain: string, cidrGroups: string[]): Promise { 66 | const templateContent = await readFile(CONFIG_TEMPLATE_PATH, 'utf-8') 67 | const allowedIPs = cidrGroups.map((g) => ` allow ${g};\n`).reduce((acc, curr) => acc + curr, '') 68 | const content = templateContent 69 | .replace(/{{domainName}}/g, domain) 70 | .replace(/{{allowedIPs}}/g, allowedIPs) 71 | return this.putFile(this.configPath, content) 72 | } 73 | 74 | async getConfig(): Promise { 75 | return this.getFile(this.configPath) 76 | } 77 | 78 | async getTemplate(): Promise { 79 | return this.getFile(CONFIG_TEMPLATE_PATH) 80 | } 81 | 82 | async putFile(path: string, content: string): Promise { 83 | let message: string | undefined 84 | let success = false 85 | try { 86 | await writeFile(path, content) 87 | message = 'Nginx configuration updated.' 88 | success = true 89 | log('info', message) 90 | await this.reload() 91 | } catch (error) { 92 | log('error', `Failed to update Nginx configuration. ${path}`, error) 93 | message = error != null ? (error as any).message ?? 'Error' : 'Unknown error' 94 | } 95 | return { success, message } 96 | } 97 | 98 | private async getFile(path: string): Promise { 99 | let config: string = '' 100 | let message: string | undefined 101 | let success = false 102 | try { 103 | config = await readFile(path, 'utf-8') 104 | success = true 105 | } catch (error) { 106 | log('error', `Failed to read configuration file. ${path}`, error) 107 | message = error != null ? (error as any).message ?? 'Error' : 'Unknown error' 108 | } 109 | return { success, config, message } 110 | } 111 | 112 | async obtainCertificates(domains: string[] = [], cloudflare?: boolean): Promise { 113 | const domainArgs = domains.map((domain) => `-d ${domain}`).join(' ') 114 | let message: string | undefined 115 | let success = false 116 | const cloudflareFlags = cloudflare 117 | ? ' --dns-cloudflare --dns-cloudflare-credentials /opt/cloudflare/credentials' 118 | : '' 119 | const command = `certbot certonly -n --email email@email.com --agree-tos${cloudflareFlags} ${domainArgs} --preferred-challenges dns-01` 120 | try { 121 | await execAsync(command) 122 | message = 'Certificates obtained successfully.' 123 | success = true 124 | log('info', message) 125 | } catch (error) { 126 | log('error', 'Failed to obtain certificates.', error) 127 | message = error != null ? (error as any).message ?? 'Error' : 'Unknown error' 128 | message = `command: ${command}\n${message}` 129 | } 130 | return { success, message } 131 | } 132 | 133 | async renewCertificates(): Promise { 134 | let message: string | undefined 135 | let success = false 136 | const command = 'certbot renew' 137 | try { 138 | await execAsync(command) 139 | message = 'Certificates renewed successfully.' 140 | success = true 141 | log('info', message) 142 | } catch (error) { 143 | log('error', 'Failed to renew certificates.', error) 144 | message = error != null ? (error as any).message ?? 'Error' : 'Unknown error' 145 | message = `command: ${command}\n${message}` 146 | } 147 | return { success, message } 148 | } 149 | } 150 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es6", 4 | "module": "commonjs", 5 | "outDir": "./dist", 6 | "rootDir": "./src", 7 | "strict": true, 8 | "esModuleInterop": true, 9 | "skipLibCheck": true, 10 | "forceConsistentCasingInFileNames": true 11 | }, 12 | "include": ["src/**/*"], 13 | "exclude": ["node_modules"] 14 | } -------------------------------------------------------------------------------- /yarn.lock: -------------------------------------------------------------------------------- 1 | # THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. 2 | # yarn lockfile v1 3 | 4 | 5 | "@cspotcode/source-map-support@^0.8.0": 6 | version "0.8.1" 7 | resolved "https://registry.yarnpkg.com/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz#00629c35a688e05a88b1cda684fb9d5e73f000a1" 8 | integrity sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw== 9 | dependencies: 10 | "@jridgewell/trace-mapping" "0.3.9" 11 | 12 | "@jridgewell/resolve-uri@^3.0.3": 13 | version "3.1.2" 14 | resolved "https://registry.yarnpkg.com/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz#7a0ee601f60f99a20c7c7c5ff0c80388c1189bd6" 15 | integrity sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw== 16 | 17 | "@jridgewell/sourcemap-codec@^1.4.10": 18 | version "1.5.0" 19 | resolved "https://registry.yarnpkg.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.0.tgz#3188bcb273a414b0d215fd22a58540b989b9409a" 20 | integrity sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ== 21 | 22 | "@jridgewell/trace-mapping@0.3.9": 23 | version "0.3.9" 24 | resolved "https://registry.yarnpkg.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz#6534fd5933a53ba7cbf3a17615e273a0d1273ff9" 25 | integrity sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ== 26 | dependencies: 27 | "@jridgewell/resolve-uri" "^3.0.3" 28 | "@jridgewell/sourcemap-codec" "^1.4.10" 29 | 30 | "@tsconfig/node10@^1.0.7": 31 | version "1.0.11" 32 | resolved "https://registry.yarnpkg.com/@tsconfig/node10/-/node10-1.0.11.tgz#6ee46400685f130e278128c7b38b7e031ff5b2f2" 33 | integrity sha512-DcRjDCujK/kCk/cUe8Xz8ZSpm8mS3mNNpta+jGCA6USEDfktlNvm1+IuZ9eTcDbNk41BHwpHHeW+N1lKCz4zOw== 34 | 35 | "@tsconfig/node12@^1.0.7": 36 | version "1.0.11" 37 | resolved "https://registry.yarnpkg.com/@tsconfig/node12/-/node12-1.0.11.tgz#ee3def1f27d9ed66dac6e46a295cffb0152e058d" 38 | integrity sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag== 39 | 40 | "@tsconfig/node14@^1.0.0": 41 | version "1.0.3" 42 | resolved "https://registry.yarnpkg.com/@tsconfig/node14/-/node14-1.0.3.tgz#e4386316284f00b98435bf40f72f75a09dabf6c1" 43 | integrity sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow== 44 | 45 | "@tsconfig/node16@^1.0.2": 46 | version "1.0.4" 47 | resolved "https://registry.yarnpkg.com/@tsconfig/node16/-/node16-1.0.4.tgz#0b92dcc0cc1c81f6f306a381f28e31b1a56536e9" 48 | integrity sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA== 49 | 50 | "@types/body-parser@*": 51 | version "1.19.5" 52 | resolved "https://registry.yarnpkg.com/@types/body-parser/-/body-parser-1.19.5.tgz#04ce9a3b677dc8bd681a17da1ab9835dc9d3ede4" 53 | integrity sha512-fB3Zu92ucau0iQ0JMCFQE7b/dv8Ot07NI3KaZIkIUNXq82k4eBAqUaneXfleGY9JWskeS9y+u0nXMyspcuQrCg== 54 | dependencies: 55 | "@types/connect" "*" 56 | "@types/node" "*" 57 | 58 | "@types/connect@*": 59 | version "3.4.38" 60 | resolved "https://registry.yarnpkg.com/@types/connect/-/connect-3.4.38.tgz#5ba7f3bc4fbbdeaff8dded952e5ff2cc53f8d858" 61 | integrity sha512-K6uROf1LD88uDQqJCktA4yzL1YYAK6NgfsI0v/mTgyPKWsX1CnJ0XPSDhViejru1GcRkLWb8RlzFYJRqGUbaug== 62 | dependencies: 63 | "@types/node" "*" 64 | 65 | "@types/express-serve-static-core@^4.17.33": 66 | version "4.19.5" 67 | resolved "https://registry.yarnpkg.com/@types/express-serve-static-core/-/express-serve-static-core-4.19.5.tgz#218064e321126fcf9048d1ca25dd2465da55d9c6" 68 | integrity sha512-y6W03tvrACO72aijJ5uF02FRq5cgDR9lUxddQ8vyF+GvmjJQqbzDcJngEjURc+ZsG31VI3hODNZJ2URj86pzmg== 69 | dependencies: 70 | "@types/node" "*" 71 | "@types/qs" "*" 72 | "@types/range-parser" "*" 73 | "@types/send" "*" 74 | 75 | "@types/express@^4.17.21": 76 | version "4.17.21" 77 | resolved "https://registry.yarnpkg.com/@types/express/-/express-4.17.21.tgz#c26d4a151e60efe0084b23dc3369ebc631ed192d" 78 | integrity sha512-ejlPM315qwLpaQlQDTjPdsUFSc6ZsP4AN6AlWnogPjQ7CVi7PYF3YVz+CY3jE2pwYf7E/7HlDAN0rV2GxTG0HQ== 79 | dependencies: 80 | "@types/body-parser" "*" 81 | "@types/express-serve-static-core" "^4.17.33" 82 | "@types/qs" "*" 83 | "@types/serve-static" "*" 84 | 85 | "@types/fs-extra@^11.0.4": 86 | version "11.0.4" 87 | resolved "https://registry.yarnpkg.com/@types/fs-extra/-/fs-extra-11.0.4.tgz#e16a863bb8843fba8c5004362b5a73e17becca45" 88 | integrity sha512-yTbItCNreRooED33qjunPthRcSjERP1r4MqCZc7wv0u2sUkzTFp45tgUfS5+r7FrZPdmCCNflLhVSP/o+SemsQ== 89 | dependencies: 90 | "@types/jsonfile" "*" 91 | "@types/node" "*" 92 | 93 | "@types/http-errors@*": 94 | version "2.0.4" 95 | resolved "https://registry.yarnpkg.com/@types/http-errors/-/http-errors-2.0.4.tgz#7eb47726c391b7345a6ec35ad7f4de469cf5ba4f" 96 | integrity sha512-D0CFMMtydbJAegzOyHjtiKPLlvnm3iTZyZRSZoLq2mRhDdmLfIWOCYPfQJ4cu2erKghU++QvjcUjp/5h7hESpA== 97 | 98 | "@types/jsonfile@*": 99 | version "6.1.4" 100 | resolved "https://registry.yarnpkg.com/@types/jsonfile/-/jsonfile-6.1.4.tgz#614afec1a1164e7d670b4a7ad64df3e7beb7b702" 101 | integrity sha512-D5qGUYwjvnNNextdU59/+fI+spnwtTFmyQP0h+PfIOSkNfpU6AOICUOkm4i0OnSk+NyjdPJrxCDro0sJsWlRpQ== 102 | dependencies: 103 | "@types/node" "*" 104 | 105 | "@types/jsonwebtoken@^9.0.2": 106 | version "9.0.6" 107 | resolved "https://registry.yarnpkg.com/@types/jsonwebtoken/-/jsonwebtoken-9.0.6.tgz#d1af3544d99ad992fb6681bbe60676e06b032bd3" 108 | integrity sha512-/5hndP5dCjloafCXns6SZyESp3Ldq7YjH3zwzwczYnjxIT0Fqzk5ROSYVGfFyczIue7IUEj8hkvLbPoLQ18vQw== 109 | dependencies: 110 | "@types/node" "*" 111 | 112 | "@types/mime@^1": 113 | version "1.3.5" 114 | resolved "https://registry.yarnpkg.com/@types/mime/-/mime-1.3.5.tgz#1ef302e01cf7d2b5a0fa526790c9123bf1d06690" 115 | integrity sha512-/pyBZWSLD2n0dcHE3hq8s8ZvcETHtEuF+3E7XVt0Ig2nvsVQXdghHVcEkIWjy9A0wKfTn97a/PSDYohKIlnP/w== 116 | 117 | "@types/node@*", "@types/node@^20.12.7": 118 | version "20.14.10" 119 | resolved "https://registry.yarnpkg.com/@types/node/-/node-20.14.10.tgz#a1a218290f1b6428682e3af044785e5874db469a" 120 | integrity sha512-MdiXf+nDuMvY0gJKxyfZ7/6UFsETO7mGKF54MVD/ekJS6HdFtpZFBgrh6Pseu64XTb2MLyFPlbW6hj8HYRQNOQ== 121 | dependencies: 122 | undici-types "~5.26.4" 123 | 124 | "@types/qs@*": 125 | version "6.9.15" 126 | resolved "https://registry.yarnpkg.com/@types/qs/-/qs-6.9.15.tgz#adde8a060ec9c305a82de1babc1056e73bd64dce" 127 | integrity sha512-uXHQKES6DQKKCLh441Xv/dwxOq1TVS3JPUMlEqoEglvlhR6Mxnlew/Xq/LRVHpLyk7iK3zODe1qYHIMltO7XGg== 128 | 129 | "@types/range-parser@*": 130 | version "1.2.7" 131 | resolved "https://registry.yarnpkg.com/@types/range-parser/-/range-parser-1.2.7.tgz#50ae4353eaaddc04044279812f52c8c65857dbcb" 132 | integrity sha512-hKormJbkJqzQGhziax5PItDUTMAM9uE2XXQmM37dyd4hVM+5aVl7oVxMVUiVQn2oCQFN/LKCZdvSM0pFRqbSmQ== 133 | 134 | "@types/send@*": 135 | version "0.17.4" 136 | resolved "https://registry.yarnpkg.com/@types/send/-/send-0.17.4.tgz#6619cd24e7270793702e4e6a4b958a9010cfc57a" 137 | integrity sha512-x2EM6TJOybec7c52BX0ZspPodMsQUd5L6PRwOunVyVUhXiBSKf3AezDL8Dgvgt5o0UfKNfuA0eMLr2wLT4AiBA== 138 | dependencies: 139 | "@types/mime" "^1" 140 | "@types/node" "*" 141 | 142 | "@types/serve-static@*": 143 | version "1.15.7" 144 | resolved "https://registry.yarnpkg.com/@types/serve-static/-/serve-static-1.15.7.tgz#22174bbd74fb97fe303109738e9b5c2f3064f714" 145 | integrity sha512-W8Ym+h8nhuRwaKPaDw34QUkwsGi6Rc4yYqvKFo5rm2FUEhCFbzVWrxXUxuKK8TASjWsysJY0nsmNCGhCOIsrOw== 146 | dependencies: 147 | "@types/http-errors" "*" 148 | "@types/node" "*" 149 | "@types/send" "*" 150 | 151 | "@types/strip-bom@^3.0.0": 152 | version "3.0.0" 153 | resolved "https://registry.yarnpkg.com/@types/strip-bom/-/strip-bom-3.0.0.tgz#14a8ec3956c2e81edb7520790aecf21c290aebd2" 154 | integrity sha512-xevGOReSYGM7g/kUBZzPqCrR/KYAo+F0yiPc85WFTJa0MSLtyFTVTU6cJu/aV4mid7IffDIWqo69THF2o4JiEQ== 155 | 156 | "@types/strip-json-comments@0.0.30": 157 | version "0.0.30" 158 | resolved "https://registry.yarnpkg.com/@types/strip-json-comments/-/strip-json-comments-0.0.30.tgz#9aa30c04db212a9a0649d6ae6fd50accc40748a1" 159 | integrity sha512-7NQmHra/JILCd1QqpSzl8+mJRc8ZHz3uDm8YV1Ks9IhK0epEiTw8aIErbvH9PI+6XbqhyIQy3462nEsn7UVzjQ== 160 | 161 | accepts@~1.3.8: 162 | version "1.3.8" 163 | resolved "https://registry.yarnpkg.com/accepts/-/accepts-1.3.8.tgz#0bf0be125b67014adcb0b0921e62db7bffe16b2e" 164 | integrity sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw== 165 | dependencies: 166 | mime-types "~2.1.34" 167 | negotiator "0.6.3" 168 | 169 | acorn-walk@^8.1.1: 170 | version "8.3.3" 171 | resolved "https://registry.yarnpkg.com/acorn-walk/-/acorn-walk-8.3.3.tgz#9caeac29eefaa0c41e3d4c65137de4d6f34df43e" 172 | integrity sha512-MxXdReSRhGO7VlFe1bRG/oI7/mdLV9B9JJT0N8vZOhF7gFRR5l3M8W9G8JxmKV+JC5mGqJ0QvqfSOLsCPa4nUw== 173 | dependencies: 174 | acorn "^8.11.0" 175 | 176 | acorn@^8.11.0, acorn@^8.4.1: 177 | version "8.12.1" 178 | resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.12.1.tgz#71616bdccbe25e27a54439e0046e89ca76df2248" 179 | integrity sha512-tcpGyI9zbizT9JbV6oYE477V6mTlXvvi0T0G3SNIYE2apm/G5huBa1+K89VGeovbg+jycCrfhl3ADxErOuO6Jg== 180 | 181 | anymatch@~3.1.2: 182 | version "3.1.3" 183 | resolved "https://registry.yarnpkg.com/anymatch/-/anymatch-3.1.3.tgz#790c58b19ba1720a84205b57c618d5ad8524973e" 184 | integrity sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw== 185 | dependencies: 186 | normalize-path "^3.0.0" 187 | picomatch "^2.0.4" 188 | 189 | arg@^4.1.0: 190 | version "4.1.3" 191 | resolved "https://registry.yarnpkg.com/arg/-/arg-4.1.3.tgz#269fc7ad5b8e42cb63c896d5666017261c144089" 192 | integrity sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA== 193 | 194 | array-flatten@1.1.1: 195 | version "1.1.1" 196 | resolved "https://registry.yarnpkg.com/array-flatten/-/array-flatten-1.1.1.tgz#9a5f699051b1e7073328f2a008968b64ea2955d2" 197 | integrity sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg== 198 | 199 | asynckit@^0.4.0: 200 | version "0.4.0" 201 | resolved "https://registry.yarnpkg.com/asynckit/-/asynckit-0.4.0.tgz#c79ed97f7f34cb8f2ba1bc9790bcc366474b4b79" 202 | integrity sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q== 203 | 204 | axios@^1.7.2: 205 | version "1.7.2" 206 | resolved "https://registry.yarnpkg.com/axios/-/axios-1.7.2.tgz#b625db8a7051fbea61c35a3cbb3a1daa7b9c7621" 207 | integrity sha512-2A8QhOMrbomlDuiLeK9XibIBzuHeRcqqNOHp0Cyp5EoJ1IFDh+XZH3A6BkXtv0K4gFGCI0Y4BM7B1wOEi0Rmgw== 208 | dependencies: 209 | follow-redirects "^1.15.6" 210 | form-data "^4.0.0" 211 | proxy-from-env "^1.1.0" 212 | 213 | balanced-match@^1.0.0: 214 | version "1.0.2" 215 | resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.2.tgz#e83e3a7e3f300b34cb9d87f615fa0cbf357690ee" 216 | integrity sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw== 217 | 218 | binary-extensions@^2.0.0: 219 | version "2.3.0" 220 | resolved "https://registry.yarnpkg.com/binary-extensions/-/binary-extensions-2.3.0.tgz#f6e14a97858d327252200242d4ccfe522c445522" 221 | integrity sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw== 222 | 223 | body-parser@1.20.2: 224 | version "1.20.2" 225 | resolved "https://registry.yarnpkg.com/body-parser/-/body-parser-1.20.2.tgz#6feb0e21c4724d06de7ff38da36dad4f57a747fd" 226 | integrity sha512-ml9pReCu3M61kGlqoTm2umSXTlRTuGTx0bfYj+uIUKKYycG5NtSbeetV3faSU6R7ajOPw0g/J1PvK4qNy7s5bA== 227 | dependencies: 228 | bytes "3.1.2" 229 | content-type "~1.0.5" 230 | debug "2.6.9" 231 | depd "2.0.0" 232 | destroy "1.2.0" 233 | http-errors "2.0.0" 234 | iconv-lite "0.4.24" 235 | on-finished "2.4.1" 236 | qs "6.11.0" 237 | raw-body "2.5.2" 238 | type-is "~1.6.18" 239 | unpipe "1.0.0" 240 | 241 | brace-expansion@^1.1.7: 242 | version "1.1.11" 243 | resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.11.tgz#3c7fcbf529d87226f3d2f52b966ff5271eb441dd" 244 | integrity sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA== 245 | dependencies: 246 | balanced-match "^1.0.0" 247 | concat-map "0.0.1" 248 | 249 | braces@~3.0.2: 250 | version "3.0.3" 251 | resolved "https://registry.yarnpkg.com/braces/-/braces-3.0.3.tgz#490332f40919452272d55a8480adc0c441358789" 252 | integrity sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA== 253 | dependencies: 254 | fill-range "^7.1.1" 255 | 256 | buffer-equal-constant-time@1.0.1: 257 | version "1.0.1" 258 | resolved "https://registry.yarnpkg.com/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz#f8e71132f7ffe6e01a5c9697a4c6f3e48d5cc819" 259 | integrity sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA== 260 | 261 | buffer-from@^1.0.0: 262 | version "1.1.2" 263 | resolved "https://registry.yarnpkg.com/buffer-from/-/buffer-from-1.1.2.tgz#2b146a6fd72e80b4f55d255f35ed59a3a9a41bd5" 264 | integrity sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ== 265 | 266 | bytes@3.1.2: 267 | version "3.1.2" 268 | resolved "https://registry.yarnpkg.com/bytes/-/bytes-3.1.2.tgz#8b0beeb98605adf1b128fa4386403c009e0221a5" 269 | integrity sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg== 270 | 271 | call-bind@^1.0.7: 272 | version "1.0.7" 273 | resolved "https://registry.yarnpkg.com/call-bind/-/call-bind-1.0.7.tgz#06016599c40c56498c18769d2730be242b6fa3b9" 274 | integrity sha512-GHTSNSYICQ7scH7sZ+M2rFopRoLh8t2bLSW6BbgrtLsahOIB5iyAVJf9GjWK3cYTDaMj4XdBpM1cA6pIS0Kv2w== 275 | dependencies: 276 | es-define-property "^1.0.0" 277 | es-errors "^1.3.0" 278 | function-bind "^1.1.2" 279 | get-intrinsic "^1.2.4" 280 | set-function-length "^1.2.1" 281 | 282 | chokidar@^3.5.1: 283 | version "3.6.0" 284 | resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-3.6.0.tgz#197c6cc669ef2a8dc5e7b4d97ee4e092c3eb0d5b" 285 | integrity sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw== 286 | dependencies: 287 | anymatch "~3.1.2" 288 | braces "~3.0.2" 289 | glob-parent "~5.1.2" 290 | is-binary-path "~2.1.0" 291 | is-glob "~4.0.1" 292 | normalize-path "~3.0.0" 293 | readdirp "~3.6.0" 294 | optionalDependencies: 295 | fsevents "~2.3.2" 296 | 297 | combined-stream@^1.0.8: 298 | version "1.0.8" 299 | resolved "https://registry.yarnpkg.com/combined-stream/-/combined-stream-1.0.8.tgz#c3d45a8b34fd730631a110a8a2520682b31d5a7f" 300 | integrity sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg== 301 | dependencies: 302 | delayed-stream "~1.0.0" 303 | 304 | concat-map@0.0.1: 305 | version "0.0.1" 306 | resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" 307 | integrity sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg== 308 | 309 | content-disposition@0.5.4: 310 | version "0.5.4" 311 | resolved "https://registry.yarnpkg.com/content-disposition/-/content-disposition-0.5.4.tgz#8b82b4efac82512a02bb0b1dcec9d2c5e8eb5bfe" 312 | integrity sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ== 313 | dependencies: 314 | safe-buffer "5.2.1" 315 | 316 | content-type@~1.0.4, content-type@~1.0.5: 317 | version "1.0.5" 318 | resolved "https://registry.yarnpkg.com/content-type/-/content-type-1.0.5.tgz#8b773162656d1d1086784c8f23a54ce6d73d7918" 319 | integrity sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA== 320 | 321 | cookie-signature@1.0.6: 322 | version "1.0.6" 323 | resolved "https://registry.yarnpkg.com/cookie-signature/-/cookie-signature-1.0.6.tgz#e303a882b342cc3ee8ca513a79999734dab3ae2c" 324 | integrity sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ== 325 | 326 | cookie@0.6.0: 327 | version "0.6.0" 328 | resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.6.0.tgz#2798b04b071b0ecbff0dbb62a505a8efa4e19051" 329 | integrity sha512-U71cyTamuh1CRNCfpGY6to28lxvNwPG4Guz/EVjgf3Jmzv0vlDp1atT9eS5dDjMYHucpHbWns6Lwf3BKz6svdw== 330 | 331 | create-require@^1.1.0: 332 | version "1.1.1" 333 | resolved "https://registry.yarnpkg.com/create-require/-/create-require-1.1.1.tgz#c1d7e8f1e5f6cfc9ff65f9cd352d37348756c333" 334 | integrity sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ== 335 | 336 | debug@2.6.9: 337 | version "2.6.9" 338 | resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f" 339 | integrity sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA== 340 | dependencies: 341 | ms "2.0.0" 342 | 343 | define-data-property@^1.1.4: 344 | version "1.1.4" 345 | resolved "https://registry.yarnpkg.com/define-data-property/-/define-data-property-1.1.4.tgz#894dc141bb7d3060ae4366f6a0107e68fbe48c5e" 346 | integrity sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A== 347 | dependencies: 348 | es-define-property "^1.0.0" 349 | es-errors "^1.3.0" 350 | gopd "^1.0.1" 351 | 352 | delayed-stream@~1.0.0: 353 | version "1.0.0" 354 | resolved "https://registry.yarnpkg.com/delayed-stream/-/delayed-stream-1.0.0.tgz#df3ae199acadfb7d440aaae0b29e2272b24ec619" 355 | integrity sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ== 356 | 357 | depd@2.0.0: 358 | version "2.0.0" 359 | resolved "https://registry.yarnpkg.com/depd/-/depd-2.0.0.tgz#b696163cc757560d09cf22cc8fad1571b79e76df" 360 | integrity sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw== 361 | 362 | destroy@1.2.0: 363 | version "1.2.0" 364 | resolved "https://registry.yarnpkg.com/destroy/-/destroy-1.2.0.tgz#4803735509ad8be552934c67df614f94e66fa015" 365 | integrity sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg== 366 | 367 | diff@^4.0.1: 368 | version "4.0.2" 369 | resolved "https://registry.yarnpkg.com/diff/-/diff-4.0.2.tgz#60f3aecb89d5fae520c11aa19efc2bb982aade7d" 370 | integrity sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A== 371 | 372 | dotenv@^10.0.0: 373 | version "10.0.0" 374 | resolved "https://registry.yarnpkg.com/dotenv/-/dotenv-10.0.0.tgz#3d4227b8fb95f81096cdd2b66653fb2c7085ba81" 375 | integrity sha512-rlBi9d8jpv9Sf1klPjNfFAuWDjKLwTIJJ/VxtoTwIR6hnZxcEOQCZg2oIL3MWBYw5GpUDKOEnND7LXTbIpQ03Q== 376 | 377 | dynamic-dedupe@^0.3.0: 378 | version "0.3.0" 379 | resolved "https://registry.yarnpkg.com/dynamic-dedupe/-/dynamic-dedupe-0.3.0.tgz#06e44c223f5e4e94d78ef9db23a6515ce2f962a1" 380 | integrity sha512-ssuANeD+z97meYOqd50e04Ze5qp4bPqo8cCkI4TRjZkzAUgIDTrXV1R8QCdINpiI+hw14+rYazvTRdQrz0/rFQ== 381 | dependencies: 382 | xtend "^4.0.0" 383 | 384 | ecdsa-sig-formatter@1.0.11: 385 | version "1.0.11" 386 | resolved "https://registry.yarnpkg.com/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz#ae0f0fa2d85045ef14a817daa3ce9acd0489e5bf" 387 | integrity sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ== 388 | dependencies: 389 | safe-buffer "^5.0.1" 390 | 391 | ee-first@1.1.1: 392 | version "1.1.1" 393 | resolved "https://registry.yarnpkg.com/ee-first/-/ee-first-1.1.1.tgz#590c61156b0ae2f4f0255732a158b266bc56b21d" 394 | integrity sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow== 395 | 396 | encodeurl@~1.0.2: 397 | version "1.0.2" 398 | resolved "https://registry.yarnpkg.com/encodeurl/-/encodeurl-1.0.2.tgz#ad3ff4c86ec2d029322f5a02c3a9a606c95b3f59" 399 | integrity sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w== 400 | 401 | es-define-property@^1.0.0: 402 | version "1.0.0" 403 | resolved "https://registry.yarnpkg.com/es-define-property/-/es-define-property-1.0.0.tgz#c7faefbdff8b2696cf5f46921edfb77cc4ba3845" 404 | integrity sha512-jxayLKShrEqqzJ0eumQbVhTYQM27CfT1T35+gCgDFoL82JLsXqTJ76zv6A0YLOgEnLUMvLzsDsGIrl8NFpT2gQ== 405 | dependencies: 406 | get-intrinsic "^1.2.4" 407 | 408 | es-errors@^1.3.0: 409 | version "1.3.0" 410 | resolved "https://registry.yarnpkg.com/es-errors/-/es-errors-1.3.0.tgz#05f75a25dab98e4fb1dcd5e1472c0546d5057c8f" 411 | integrity sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw== 412 | 413 | escape-html@~1.0.3: 414 | version "1.0.3" 415 | resolved "https://registry.yarnpkg.com/escape-html/-/escape-html-1.0.3.tgz#0258eae4d3d0c0974de1c169188ef0051d1d1988" 416 | integrity sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow== 417 | 418 | etag@~1.8.1: 419 | version "1.8.1" 420 | resolved "https://registry.yarnpkg.com/etag/-/etag-1.8.1.tgz#41ae2eeb65efa62268aebfea83ac7d79299b0887" 421 | integrity sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg== 422 | 423 | express@^4.19.2: 424 | version "4.19.2" 425 | resolved "https://registry.yarnpkg.com/express/-/express-4.19.2.tgz#e25437827a3aa7f2a827bc8171bbbb664a356465" 426 | integrity sha512-5T6nhjsT+EOMzuck8JjBHARTHfMht0POzlA60WV2pMD3gyXw2LZnZ+ueGdNxG+0calOJcWKbpFcuzLZ91YWq9Q== 427 | dependencies: 428 | accepts "~1.3.8" 429 | array-flatten "1.1.1" 430 | body-parser "1.20.2" 431 | content-disposition "0.5.4" 432 | content-type "~1.0.4" 433 | cookie "0.6.0" 434 | cookie-signature "1.0.6" 435 | debug "2.6.9" 436 | depd "2.0.0" 437 | encodeurl "~1.0.2" 438 | escape-html "~1.0.3" 439 | etag "~1.8.1" 440 | finalhandler "1.2.0" 441 | fresh "0.5.2" 442 | http-errors "2.0.0" 443 | merge-descriptors "1.0.1" 444 | methods "~1.1.2" 445 | on-finished "2.4.1" 446 | parseurl "~1.3.3" 447 | path-to-regexp "0.1.7" 448 | proxy-addr "~2.0.7" 449 | qs "6.11.0" 450 | range-parser "~1.2.1" 451 | safe-buffer "5.2.1" 452 | send "0.18.0" 453 | serve-static "1.15.0" 454 | setprototypeof "1.2.0" 455 | statuses "2.0.1" 456 | type-is "~1.6.18" 457 | utils-merge "1.0.1" 458 | vary "~1.1.2" 459 | 460 | fill-range@^7.1.1: 461 | version "7.1.1" 462 | resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-7.1.1.tgz#44265d3cac07e3ea7dc247516380643754a05292" 463 | integrity sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg== 464 | dependencies: 465 | to-regex-range "^5.0.1" 466 | 467 | finalhandler@1.2.0: 468 | version "1.2.0" 469 | resolved "https://registry.yarnpkg.com/finalhandler/-/finalhandler-1.2.0.tgz#7d23fe5731b207b4640e4fcd00aec1f9207a7b32" 470 | integrity sha512-5uXcUVftlQMFnWC9qu/svkWv3GTd2PfUhK/3PLkYNAe7FbqJMt3515HaxE6eRL74GdsriiwujiawdaB1BpEISg== 471 | dependencies: 472 | debug "2.6.9" 473 | encodeurl "~1.0.2" 474 | escape-html "~1.0.3" 475 | on-finished "2.4.1" 476 | parseurl "~1.3.3" 477 | statuses "2.0.1" 478 | unpipe "~1.0.0" 479 | 480 | follow-redirects@^1.15.6: 481 | version "1.15.6" 482 | resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.15.6.tgz#7f815c0cda4249c74ff09e95ef97c23b5fd0399b" 483 | integrity sha512-wWN62YITEaOpSK584EZXJafH1AGpO8RVgElfkuXbTOrPX4fIfOyEpW/CsiNd8JdYrAoOvafRTOEnvsO++qCqFA== 484 | 485 | form-data@^4.0.0: 486 | version "4.0.0" 487 | resolved "https://registry.yarnpkg.com/form-data/-/form-data-4.0.0.tgz#93919daeaf361ee529584b9b31664dc12c9fa452" 488 | integrity sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww== 489 | dependencies: 490 | asynckit "^0.4.0" 491 | combined-stream "^1.0.8" 492 | mime-types "^2.1.12" 493 | 494 | forwarded@0.2.0: 495 | version "0.2.0" 496 | resolved "https://registry.yarnpkg.com/forwarded/-/forwarded-0.2.0.tgz#2269936428aad4c15c7ebe9779a84bf0b2a81811" 497 | integrity sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow== 498 | 499 | fresh@0.5.2: 500 | version "0.5.2" 501 | resolved "https://registry.yarnpkg.com/fresh/-/fresh-0.5.2.tgz#3d8cadd90d976569fa835ab1f8e4b23a105605a7" 502 | integrity sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q== 503 | 504 | fs-extra@^11.2.0: 505 | version "11.2.0" 506 | resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-11.2.0.tgz#e70e17dfad64232287d01929399e0ea7c86b0e5b" 507 | integrity sha512-PmDi3uwK5nFuXh7XDTlVnS17xJS7vW36is2+w3xcv8SVxiB4NyATf4ctkVY5bkSjX0Y4nbvZCq1/EjtEyr9ktw== 508 | dependencies: 509 | graceful-fs "^4.2.0" 510 | jsonfile "^6.0.1" 511 | universalify "^2.0.0" 512 | 513 | fs.realpath@^1.0.0: 514 | version "1.0.0" 515 | resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f" 516 | integrity sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw== 517 | 518 | fsevents@~2.3.2: 519 | version "2.3.3" 520 | resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.3.3.tgz#cac6407785d03675a2a5e1a5305c697b347d90d6" 521 | integrity sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw== 522 | 523 | function-bind@^1.1.2: 524 | version "1.1.2" 525 | resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.2.tgz#2c02d864d97f3ea6c8830c464cbd11ab6eab7a1c" 526 | integrity sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA== 527 | 528 | get-intrinsic@^1.1.3, get-intrinsic@^1.2.4: 529 | version "1.2.4" 530 | resolved "https://registry.yarnpkg.com/get-intrinsic/-/get-intrinsic-1.2.4.tgz#e385f5a4b5227d449c3eabbad05494ef0abbeadd" 531 | integrity sha512-5uYhsJH8VJBTv7oslg4BznJYhDoRI6waYCxMmCdnTrcCrHA/fCFKoTFz2JKKE0HdDFUF7/oQuhzumXJK7paBRQ== 532 | dependencies: 533 | es-errors "^1.3.0" 534 | function-bind "^1.1.2" 535 | has-proto "^1.0.1" 536 | has-symbols "^1.0.3" 537 | hasown "^2.0.0" 538 | 539 | glob-parent@~5.1.2: 540 | version "5.1.2" 541 | resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-5.1.2.tgz#869832c58034fe68a4093c17dc15e8340d8401c4" 542 | integrity sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow== 543 | dependencies: 544 | is-glob "^4.0.1" 545 | 546 | glob@^7.1.3: 547 | version "7.2.3" 548 | resolved "https://registry.yarnpkg.com/glob/-/glob-7.2.3.tgz#b8df0fb802bbfa8e89bd1d938b4e16578ed44f2b" 549 | integrity sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q== 550 | dependencies: 551 | fs.realpath "^1.0.0" 552 | inflight "^1.0.4" 553 | inherits "2" 554 | minimatch "^3.1.1" 555 | once "^1.3.0" 556 | path-is-absolute "^1.0.0" 557 | 558 | gopd@^1.0.1: 559 | version "1.0.1" 560 | resolved "https://registry.yarnpkg.com/gopd/-/gopd-1.0.1.tgz#29ff76de69dac7489b7c0918a5788e56477c332c" 561 | integrity sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA== 562 | dependencies: 563 | get-intrinsic "^1.1.3" 564 | 565 | graceful-fs@^4.1.6, graceful-fs@^4.2.0: 566 | version "4.2.11" 567 | resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.11.tgz#4183e4e8bf08bb6e05bbb2f7d2e0c8f712ca40e3" 568 | integrity sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ== 569 | 570 | has-property-descriptors@^1.0.2: 571 | version "1.0.2" 572 | resolved "https://registry.yarnpkg.com/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz#963ed7d071dc7bf5f084c5bfbe0d1b6222586854" 573 | integrity sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg== 574 | dependencies: 575 | es-define-property "^1.0.0" 576 | 577 | has-proto@^1.0.1: 578 | version "1.0.3" 579 | resolved "https://registry.yarnpkg.com/has-proto/-/has-proto-1.0.3.tgz#b31ddfe9b0e6e9914536a6ab286426d0214f77fd" 580 | integrity sha512-SJ1amZAJUiZS+PhsVLf5tGydlaVB8EdFpaSO4gmiUKUOxk8qzn5AIy4ZeJUmh22znIdk/uMAUT2pl3FxzVUH+Q== 581 | 582 | has-symbols@^1.0.3: 583 | version "1.0.3" 584 | resolved "https://registry.yarnpkg.com/has-symbols/-/has-symbols-1.0.3.tgz#bb7b2c4349251dce87b125f7bdf874aa7c8b39f8" 585 | integrity sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A== 586 | 587 | hasown@^2.0.0, hasown@^2.0.2: 588 | version "2.0.2" 589 | resolved "https://registry.yarnpkg.com/hasown/-/hasown-2.0.2.tgz#003eaf91be7adc372e84ec59dc37252cedb80003" 590 | integrity sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ== 591 | dependencies: 592 | function-bind "^1.1.2" 593 | 594 | http-errors@2.0.0: 595 | version "2.0.0" 596 | resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-2.0.0.tgz#b7774a1486ef73cf7667ac9ae0858c012c57b9d3" 597 | integrity sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ== 598 | dependencies: 599 | depd "2.0.0" 600 | inherits "2.0.4" 601 | setprototypeof "1.2.0" 602 | statuses "2.0.1" 603 | toidentifier "1.0.1" 604 | 605 | iconv-lite@0.4.24: 606 | version "0.4.24" 607 | resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.24.tgz#2022b4b25fbddc21d2f524974a474aafe733908b" 608 | integrity sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA== 609 | dependencies: 610 | safer-buffer ">= 2.1.2 < 3" 611 | 612 | inflight@^1.0.4: 613 | version "1.0.6" 614 | resolved "https://registry.yarnpkg.com/inflight/-/inflight-1.0.6.tgz#49bd6331d7d02d0c09bc910a1075ba8165b56df9" 615 | integrity sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA== 616 | dependencies: 617 | once "^1.3.0" 618 | wrappy "1" 619 | 620 | inherits@2, inherits@2.0.4: 621 | version "2.0.4" 622 | resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c" 623 | integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== 624 | 625 | ipaddr.js@1.9.1: 626 | version "1.9.1" 627 | resolved "https://registry.yarnpkg.com/ipaddr.js/-/ipaddr.js-1.9.1.tgz#bff38543eeb8984825079ff3a2a8e6cbd46781b3" 628 | integrity sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g== 629 | 630 | is-binary-path@~2.1.0: 631 | version "2.1.0" 632 | resolved "https://registry.yarnpkg.com/is-binary-path/-/is-binary-path-2.1.0.tgz#ea1f7f3b80f064236e83470f86c09c254fb45b09" 633 | integrity sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw== 634 | dependencies: 635 | binary-extensions "^2.0.0" 636 | 637 | is-core-module@^2.13.0: 638 | version "2.14.0" 639 | resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.14.0.tgz#43b8ef9f46a6a08888db67b1ffd4ec9e3dfd59d1" 640 | integrity sha512-a5dFJih5ZLYlRtDc0dZWP7RiKr6xIKzmn/oAYCDvdLThadVgyJwlaoQPmRtMSpz+rk0OGAgIu+TcM9HUF0fk1A== 641 | dependencies: 642 | hasown "^2.0.2" 643 | 644 | is-extglob@^2.1.1: 645 | version "2.1.1" 646 | resolved "https://registry.yarnpkg.com/is-extglob/-/is-extglob-2.1.1.tgz#a88c02535791f02ed37c76a1b9ea9773c833f8c2" 647 | integrity sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ== 648 | 649 | is-glob@^4.0.1, is-glob@~4.0.1: 650 | version "4.0.3" 651 | resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-4.0.3.tgz#64f61e42cbbb2eec2071a9dac0b28ba1e65d5084" 652 | integrity sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg== 653 | dependencies: 654 | is-extglob "^2.1.1" 655 | 656 | is-number@^7.0.0: 657 | version "7.0.0" 658 | resolved "https://registry.yarnpkg.com/is-number/-/is-number-7.0.0.tgz#7535345b896734d5f80c4d06c50955527a14f12b" 659 | integrity sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng== 660 | 661 | jsonfile@^6.0.1: 662 | version "6.1.0" 663 | resolved "https://registry.yarnpkg.com/jsonfile/-/jsonfile-6.1.0.tgz#bc55b2634793c679ec6403094eb13698a6ec0aae" 664 | integrity sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ== 665 | dependencies: 666 | universalify "^2.0.0" 667 | optionalDependencies: 668 | graceful-fs "^4.1.6" 669 | 670 | jsonwebtoken@^9.0.2: 671 | version "9.0.2" 672 | resolved "https://registry.yarnpkg.com/jsonwebtoken/-/jsonwebtoken-9.0.2.tgz#65ff91f4abef1784697d40952bb1998c504caaf3" 673 | integrity sha512-PRp66vJ865SSqOlgqS8hujT5U4AOgMfhrwYIuIhfKaoSCZcirrmASQr8CX7cUg+RMih+hgznrjp99o+W4pJLHQ== 674 | dependencies: 675 | jws "^3.2.2" 676 | lodash.includes "^4.3.0" 677 | lodash.isboolean "^3.0.3" 678 | lodash.isinteger "^4.0.4" 679 | lodash.isnumber "^3.0.3" 680 | lodash.isplainobject "^4.0.6" 681 | lodash.isstring "^4.0.1" 682 | lodash.once "^4.0.0" 683 | ms "^2.1.1" 684 | semver "^7.5.4" 685 | 686 | jwa@^1.4.1: 687 | version "1.4.1" 688 | resolved "https://registry.yarnpkg.com/jwa/-/jwa-1.4.1.tgz#743c32985cb9e98655530d53641b66c8645b039a" 689 | integrity sha512-qiLX/xhEEFKUAJ6FiBMbes3w9ATzyk5W7Hvzpa/SLYdxNtng+gcurvrI7TbACjIXlsJyr05/S1oUhZrc63evQA== 690 | dependencies: 691 | buffer-equal-constant-time "1.0.1" 692 | ecdsa-sig-formatter "1.0.11" 693 | safe-buffer "^5.0.1" 694 | 695 | jws@^3.2.2: 696 | version "3.2.2" 697 | resolved "https://registry.yarnpkg.com/jws/-/jws-3.2.2.tgz#001099f3639468c9414000e99995fa52fb478304" 698 | integrity sha512-YHlZCB6lMTllWDtSPHz/ZXTsi8S00usEV6v1tjq8tOUZzw7DpSDWVXjXDre6ed1w/pd495ODpHZYSdkRTsa0HA== 699 | dependencies: 700 | jwa "^1.4.1" 701 | safe-buffer "^5.0.1" 702 | 703 | lodash.includes@^4.3.0: 704 | version "4.3.0" 705 | resolved "https://registry.yarnpkg.com/lodash.includes/-/lodash.includes-4.3.0.tgz#60bb98a87cb923c68ca1e51325483314849f553f" 706 | integrity sha512-W3Bx6mdkRTGtlJISOvVD/lbqjTlPPUDTMnlXZFnVwi9NKJ6tiAk6LVdlhZMm17VZisqhKcgzpO5Wz91PCt5b0w== 707 | 708 | lodash.isboolean@^3.0.3: 709 | version "3.0.3" 710 | resolved "https://registry.yarnpkg.com/lodash.isboolean/-/lodash.isboolean-3.0.3.tgz#6c2e171db2a257cd96802fd43b01b20d5f5870f6" 711 | integrity sha512-Bz5mupy2SVbPHURB98VAcw+aHh4vRV5IPNhILUCsOzRmsTmSQ17jIuqopAentWoehktxGd9e/hbIXq980/1QJg== 712 | 713 | lodash.isinteger@^4.0.4: 714 | version "4.0.4" 715 | resolved "https://registry.yarnpkg.com/lodash.isinteger/-/lodash.isinteger-4.0.4.tgz#619c0af3d03f8b04c31f5882840b77b11cd68343" 716 | integrity sha512-DBwtEWN2caHQ9/imiNeEA5ys1JoRtRfY3d7V9wkqtbycnAmTvRRmbHKDV4a0EYc678/dia0jrte4tjYwVBaZUA== 717 | 718 | lodash.isnumber@^3.0.3: 719 | version "3.0.3" 720 | resolved "https://registry.yarnpkg.com/lodash.isnumber/-/lodash.isnumber-3.0.3.tgz#3ce76810c5928d03352301ac287317f11c0b1ffc" 721 | integrity sha512-QYqzpfwO3/CWf3XP+Z+tkQsfaLL/EnUlXWVkIk5FUPc4sBdTehEqZONuyRt2P67PXAk+NXmTBcc97zw9t1FQrw== 722 | 723 | lodash.isplainobject@^4.0.6: 724 | version "4.0.6" 725 | resolved "https://registry.yarnpkg.com/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz#7c526a52d89b45c45cc690b88163be0497f550cb" 726 | integrity sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA== 727 | 728 | lodash.isstring@^4.0.1: 729 | version "4.0.1" 730 | resolved "https://registry.yarnpkg.com/lodash.isstring/-/lodash.isstring-4.0.1.tgz#d527dfb5456eca7cc9bb95d5daeaf88ba54a5451" 731 | integrity sha512-0wJxfxH1wgO3GrbuP+dTTk7op+6L41QCXbGINEmD+ny/G/eCqGzxyCsh7159S+mgDDcoarnBw6PC1PS5+wUGgw== 732 | 733 | lodash.once@^4.0.0: 734 | version "4.1.1" 735 | resolved "https://registry.yarnpkg.com/lodash.once/-/lodash.once-4.1.1.tgz#0dd3971213c7c56df880977d504c88fb471a97ac" 736 | integrity sha512-Sb487aTOCr9drQVL8pIxOzVhafOjZN9UU54hiN8PU3uAiSV7lx1yYNpbNmex2PK6dSJoNTSJUUswT651yww3Mg== 737 | 738 | make-error@^1.1.1: 739 | version "1.3.6" 740 | resolved "https://registry.yarnpkg.com/make-error/-/make-error-1.3.6.tgz#2eb2e37ea9b67c4891f684a1394799af484cf7a2" 741 | integrity sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw== 742 | 743 | media-typer@0.3.0: 744 | version "0.3.0" 745 | resolved "https://registry.yarnpkg.com/media-typer/-/media-typer-0.3.0.tgz#8710d7af0aa626f8fffa1ce00168545263255748" 746 | integrity sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ== 747 | 748 | merge-descriptors@1.0.1: 749 | version "1.0.1" 750 | resolved "https://registry.yarnpkg.com/merge-descriptors/-/merge-descriptors-1.0.1.tgz#b00aaa556dd8b44568150ec9d1b953f3f90cbb61" 751 | integrity sha512-cCi6g3/Zr1iqQi6ySbseM1Xvooa98N0w31jzUYrXPX2xqObmFGHJ0tQ5u74H3mVh7wLouTseZyYIq39g8cNp1w== 752 | 753 | methods@~1.1.2: 754 | version "1.1.2" 755 | resolved "https://registry.yarnpkg.com/methods/-/methods-1.1.2.tgz#5529a4d67654134edcc5266656835b0f851afcee" 756 | integrity sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w== 757 | 758 | mime-db@1.52.0: 759 | version "1.52.0" 760 | resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.52.0.tgz#bbabcdc02859f4987301c856e3387ce5ec43bf70" 761 | integrity sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg== 762 | 763 | mime-types@^2.1.12, mime-types@~2.1.24, mime-types@~2.1.34: 764 | version "2.1.35" 765 | resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.35.tgz#381a871b62a734450660ae3deee44813f70d959a" 766 | integrity sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw== 767 | dependencies: 768 | mime-db "1.52.0" 769 | 770 | mime@1.6.0: 771 | version "1.6.0" 772 | resolved "https://registry.yarnpkg.com/mime/-/mime-1.6.0.tgz#32cd9e5c64553bd58d19a568af452acff04981b1" 773 | integrity sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg== 774 | 775 | minimatch@^3.1.1: 776 | version "3.1.2" 777 | resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.1.2.tgz#19cd194bfd3e428f049a70817c038d89ab4be35b" 778 | integrity sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw== 779 | dependencies: 780 | brace-expansion "^1.1.7" 781 | 782 | minimist@^1.2.6: 783 | version "1.2.8" 784 | resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.8.tgz#c1a464e7693302e082a075cee0c057741ac4772c" 785 | integrity sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA== 786 | 787 | mkdirp@^1.0.4: 788 | version "1.0.4" 789 | resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-1.0.4.tgz#3eb5ed62622756d79a5f0e2a221dfebad75c2f7e" 790 | integrity sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw== 791 | 792 | ms@2.0.0: 793 | version "2.0.0" 794 | resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8" 795 | integrity sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A== 796 | 797 | ms@2.1.3, ms@^2.1.1: 798 | version "2.1.3" 799 | resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.3.tgz#574c8138ce1d2b5861f0b44579dbadd60c6615b2" 800 | integrity sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA== 801 | 802 | negotiator@0.6.3: 803 | version "0.6.3" 804 | resolved "https://registry.yarnpkg.com/negotiator/-/negotiator-0.6.3.tgz#58e323a72fedc0d6f9cd4d31fe49f51479590ccd" 805 | integrity sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg== 806 | 807 | normalize-path@^3.0.0, normalize-path@~3.0.0: 808 | version "3.0.0" 809 | resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-3.0.0.tgz#0dcd69ff23a1c9b11fd0978316644a0388216a65" 810 | integrity sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA== 811 | 812 | object-inspect@^1.13.1: 813 | version "1.13.2" 814 | resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.13.2.tgz#dea0088467fb991e67af4058147a24824a3043ff" 815 | integrity sha512-IRZSRuzJiynemAXPYtPe5BoI/RESNYR7TYm50MC5Mqbd3Jmw5y790sErYw3V6SryFJD64b74qQQs9wn5Bg/k3g== 816 | 817 | on-finished@2.4.1: 818 | version "2.4.1" 819 | resolved "https://registry.yarnpkg.com/on-finished/-/on-finished-2.4.1.tgz#58c8c44116e54845ad57f14ab10b03533184ac3f" 820 | integrity sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg== 821 | dependencies: 822 | ee-first "1.1.1" 823 | 824 | once@^1.3.0: 825 | version "1.4.0" 826 | resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1" 827 | integrity sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w== 828 | dependencies: 829 | wrappy "1" 830 | 831 | parseurl@~1.3.3: 832 | version "1.3.3" 833 | resolved "https://registry.yarnpkg.com/parseurl/-/parseurl-1.3.3.tgz#9da19e7bee8d12dff0513ed5b76957793bc2e8d4" 834 | integrity sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ== 835 | 836 | path-is-absolute@^1.0.0: 837 | version "1.0.1" 838 | resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f" 839 | integrity sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg== 840 | 841 | path-parse@^1.0.7: 842 | version "1.0.7" 843 | resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.7.tgz#fbc114b60ca42b30d9daf5858e4bd68bbedb6735" 844 | integrity sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw== 845 | 846 | path-to-regexp@0.1.7: 847 | version "0.1.7" 848 | resolved "https://registry.yarnpkg.com/path-to-regexp/-/path-to-regexp-0.1.7.tgz#df604178005f522f15eb4490e7247a1bfaa67f8c" 849 | integrity sha512-5DFkuoqlv1uYQKxy8omFBeJPQcdoE07Kv2sferDCrAq1ohOU+MSDswDIbnx3YAM60qIOnYa53wBhXW0EbMonrQ== 850 | 851 | picomatch@^2.0.4, picomatch@^2.2.1: 852 | version "2.3.1" 853 | resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.3.1.tgz#3ba3833733646d9d3e4995946c1365a67fb07a42" 854 | integrity sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA== 855 | 856 | proxy-addr@~2.0.7: 857 | version "2.0.7" 858 | resolved "https://registry.yarnpkg.com/proxy-addr/-/proxy-addr-2.0.7.tgz#f19fe69ceab311eeb94b42e70e8c2070f9ba1025" 859 | integrity sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg== 860 | dependencies: 861 | forwarded "0.2.0" 862 | ipaddr.js "1.9.1" 863 | 864 | proxy-from-env@^1.1.0: 865 | version "1.1.0" 866 | resolved "https://registry.yarnpkg.com/proxy-from-env/-/proxy-from-env-1.1.0.tgz#e102f16ca355424865755d2c9e8ea4f24d58c3e2" 867 | integrity sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg== 868 | 869 | qs@6.11.0: 870 | version "6.11.0" 871 | resolved "https://registry.yarnpkg.com/qs/-/qs-6.11.0.tgz#fd0d963446f7a65e1367e01abd85429453f0c37a" 872 | integrity sha512-MvjoMCJwEarSbUYk5O+nmoSzSutSsTwF85zcHPQ9OrlFoZOYIjaqBAJIqIXjptyD5vThxGq52Xu/MaJzRkIk4Q== 873 | dependencies: 874 | side-channel "^1.0.4" 875 | 876 | range-parser@~1.2.1: 877 | version "1.2.1" 878 | resolved "https://registry.yarnpkg.com/range-parser/-/range-parser-1.2.1.tgz#3cf37023d199e1c24d1a55b84800c2f3e6468031" 879 | integrity sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg== 880 | 881 | raw-body@2.5.2: 882 | version "2.5.2" 883 | resolved "https://registry.yarnpkg.com/raw-body/-/raw-body-2.5.2.tgz#99febd83b90e08975087e8f1f9419a149366b68a" 884 | integrity sha512-8zGqypfENjCIqGhgXToC8aB2r7YrBX+AQAfIPs/Mlk+BtPTztOvTS01NRW/3Eh60J+a48lt8qsCzirQ6loCVfA== 885 | dependencies: 886 | bytes "3.1.2" 887 | http-errors "2.0.0" 888 | iconv-lite "0.4.24" 889 | unpipe "1.0.0" 890 | 891 | readdirp@~3.6.0: 892 | version "3.6.0" 893 | resolved "https://registry.yarnpkg.com/readdirp/-/readdirp-3.6.0.tgz#74a370bd857116e245b29cc97340cd431a02a6c7" 894 | integrity sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA== 895 | dependencies: 896 | picomatch "^2.2.1" 897 | 898 | resolve@^1.0.0: 899 | version "1.22.8" 900 | resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.22.8.tgz#b6c87a9f2aa06dfab52e3d70ac8cde321fa5a48d" 901 | integrity sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw== 902 | dependencies: 903 | is-core-module "^2.13.0" 904 | path-parse "^1.0.7" 905 | supports-preserve-symlinks-flag "^1.0.0" 906 | 907 | rimraf@^2.6.1: 908 | version "2.7.1" 909 | resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.7.1.tgz#35797f13a7fdadc566142c29d4f07ccad483e3ec" 910 | integrity sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w== 911 | dependencies: 912 | glob "^7.1.3" 913 | 914 | safe-buffer@5.2.1, safe-buffer@^5.0.1: 915 | version "5.2.1" 916 | resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.1.tgz#1eaf9fa9bdb1fdd4ec75f58f9cdb4e6b7827eec6" 917 | integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ== 918 | 919 | "safer-buffer@>= 2.1.2 < 3": 920 | version "2.1.2" 921 | resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a" 922 | integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg== 923 | 924 | semver@^7.5.4: 925 | version "7.6.2" 926 | resolved "https://registry.yarnpkg.com/semver/-/semver-7.6.2.tgz#1e3b34759f896e8f14d6134732ce798aeb0c6e13" 927 | integrity sha512-FNAIBWCx9qcRhoHcgcJ0gvU7SN1lYU2ZXuSfl04bSC5OpvDHFyJCjdNHomPXxjQlCBU67YW64PzY7/VIEH7F2w== 928 | 929 | send@0.18.0: 930 | version "0.18.0" 931 | resolved "https://registry.yarnpkg.com/send/-/send-0.18.0.tgz#670167cc654b05f5aa4a767f9113bb371bc706be" 932 | integrity sha512-qqWzuOjSFOuqPjFe4NOsMLafToQQwBSOEpS+FwEt3A2V3vKubTquT3vmLTQpFgMXp8AlFWFuP1qKaJZOtPpVXg== 933 | dependencies: 934 | debug "2.6.9" 935 | depd "2.0.0" 936 | destroy "1.2.0" 937 | encodeurl "~1.0.2" 938 | escape-html "~1.0.3" 939 | etag "~1.8.1" 940 | fresh "0.5.2" 941 | http-errors "2.0.0" 942 | mime "1.6.0" 943 | ms "2.1.3" 944 | on-finished "2.4.1" 945 | range-parser "~1.2.1" 946 | statuses "2.0.1" 947 | 948 | serve-static@1.15.0: 949 | version "1.15.0" 950 | resolved "https://registry.yarnpkg.com/serve-static/-/serve-static-1.15.0.tgz#faaef08cffe0a1a62f60cad0c4e513cff0ac9540" 951 | integrity sha512-XGuRDNjXUijsUL0vl6nSD7cwURuzEgglbOaFuZM9g3kwDXOWVTck0jLzjPzGD+TazWbboZYu52/9/XPdUgne9g== 952 | dependencies: 953 | encodeurl "~1.0.2" 954 | escape-html "~1.0.3" 955 | parseurl "~1.3.3" 956 | send "0.18.0" 957 | 958 | set-function-length@^1.2.1: 959 | version "1.2.2" 960 | resolved "https://registry.yarnpkg.com/set-function-length/-/set-function-length-1.2.2.tgz#aac72314198eaed975cf77b2c3b6b880695e5449" 961 | integrity sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg== 962 | dependencies: 963 | define-data-property "^1.1.4" 964 | es-errors "^1.3.0" 965 | function-bind "^1.1.2" 966 | get-intrinsic "^1.2.4" 967 | gopd "^1.0.1" 968 | has-property-descriptors "^1.0.2" 969 | 970 | setprototypeof@1.2.0: 971 | version "1.2.0" 972 | resolved "https://registry.yarnpkg.com/setprototypeof/-/setprototypeof-1.2.0.tgz#66c9a24a73f9fc28cbe66b09fed3d33dcaf1b424" 973 | integrity sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw== 974 | 975 | side-channel@^1.0.4: 976 | version "1.0.6" 977 | resolved "https://registry.yarnpkg.com/side-channel/-/side-channel-1.0.6.tgz#abd25fb7cd24baf45466406b1096b7831c9215f2" 978 | integrity sha512-fDW/EZ6Q9RiO8eFG8Hj+7u/oW+XrPTIChwCOM2+th2A6OblDtYYIpve9m+KvI9Z4C9qSEXlaGR6bTEYHReuglA== 979 | dependencies: 980 | call-bind "^1.0.7" 981 | es-errors "^1.3.0" 982 | get-intrinsic "^1.2.4" 983 | object-inspect "^1.13.1" 984 | 985 | source-map-support@^0.5.12: 986 | version "0.5.21" 987 | resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.21.tgz#04fe7c7f9e1ed2d662233c28cb2b35b9f63f6e4f" 988 | integrity sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w== 989 | dependencies: 990 | buffer-from "^1.0.0" 991 | source-map "^0.6.0" 992 | 993 | source-map@^0.6.0: 994 | version "0.6.1" 995 | resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.6.1.tgz#74722af32e9614e9c287a8d0bbde48b5e2f1a263" 996 | integrity sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g== 997 | 998 | statuses@2.0.1: 999 | version "2.0.1" 1000 | resolved "https://registry.yarnpkg.com/statuses/-/statuses-2.0.1.tgz#55cb000ccf1d48728bd23c685a063998cf1a1b63" 1001 | integrity sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ== 1002 | 1003 | strip-bom@^3.0.0: 1004 | version "3.0.0" 1005 | resolved "https://registry.yarnpkg.com/strip-bom/-/strip-bom-3.0.0.tgz#2334c18e9c759f7bdd56fdef7e9ae3d588e68ed3" 1006 | integrity sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA== 1007 | 1008 | strip-json-comments@^2.0.0: 1009 | version "2.0.1" 1010 | resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-2.0.1.tgz#3c531942e908c2697c0ec344858c286c7ca0a60a" 1011 | integrity sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ== 1012 | 1013 | supports-preserve-symlinks-flag@^1.0.0: 1014 | version "1.0.0" 1015 | resolved "https://registry.yarnpkg.com/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz#6eda4bd344a3c94aea376d4cc31bc77311039e09" 1016 | integrity sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w== 1017 | 1018 | to-regex-range@^5.0.1: 1019 | version "5.0.1" 1020 | resolved "https://registry.yarnpkg.com/to-regex-range/-/to-regex-range-5.0.1.tgz#1648c44aae7c8d988a326018ed72f5b4dd0392e4" 1021 | integrity sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ== 1022 | dependencies: 1023 | is-number "^7.0.0" 1024 | 1025 | toidentifier@1.0.1: 1026 | version "1.0.1" 1027 | resolved "https://registry.yarnpkg.com/toidentifier/-/toidentifier-1.0.1.tgz#3be34321a88a820ed1bd80dfaa33e479fbb8dd35" 1028 | integrity sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA== 1029 | 1030 | tree-kill@^1.2.2: 1031 | version "1.2.2" 1032 | resolved "https://registry.yarnpkg.com/tree-kill/-/tree-kill-1.2.2.tgz#4ca09a9092c88b73a7cdc5e8a01b507b0790a0cc" 1033 | integrity sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A== 1034 | 1035 | ts-node-dev@^2.0.0: 1036 | version "2.0.0" 1037 | resolved "https://registry.yarnpkg.com/ts-node-dev/-/ts-node-dev-2.0.0.tgz#bdd53e17ab3b5d822ef519928dc6b4a7e0f13065" 1038 | integrity sha512-ywMrhCfH6M75yftYvrvNarLEY+SUXtUvU8/0Z6llrHQVBx12GiFk5sStF8UdfE/yfzk9IAq7O5EEbTQsxlBI8w== 1039 | dependencies: 1040 | chokidar "^3.5.1" 1041 | dynamic-dedupe "^0.3.0" 1042 | minimist "^1.2.6" 1043 | mkdirp "^1.0.4" 1044 | resolve "^1.0.0" 1045 | rimraf "^2.6.1" 1046 | source-map-support "^0.5.12" 1047 | tree-kill "^1.2.2" 1048 | ts-node "^10.4.0" 1049 | tsconfig "^7.0.0" 1050 | 1051 | ts-node@^10.4.0: 1052 | version "10.9.2" 1053 | resolved "https://registry.yarnpkg.com/ts-node/-/ts-node-10.9.2.tgz#70f021c9e185bccdca820e26dc413805c101c71f" 1054 | integrity sha512-f0FFpIdcHgn8zcPSbf1dRevwt047YMnaiJM3u2w2RewrB+fob/zePZcrOyQoLMMO7aBIddLcQIEK5dYjkLnGrQ== 1055 | dependencies: 1056 | "@cspotcode/source-map-support" "^0.8.0" 1057 | "@tsconfig/node10" "^1.0.7" 1058 | "@tsconfig/node12" "^1.0.7" 1059 | "@tsconfig/node14" "^1.0.0" 1060 | "@tsconfig/node16" "^1.0.2" 1061 | acorn "^8.4.1" 1062 | acorn-walk "^8.1.1" 1063 | arg "^4.1.0" 1064 | create-require "^1.1.0" 1065 | diff "^4.0.1" 1066 | make-error "^1.1.1" 1067 | v8-compile-cache-lib "^3.0.1" 1068 | yn "3.1.1" 1069 | 1070 | tsconfig@^7.0.0: 1071 | version "7.0.0" 1072 | resolved "https://registry.yarnpkg.com/tsconfig/-/tsconfig-7.0.0.tgz#84538875a4dc216e5c4a5432b3a4dec3d54e91b7" 1073 | integrity sha512-vZXmzPrL+EmC4T/4rVlT2jNVMWCi/O4DIiSj3UHg1OE5kCKbk4mfrXc6dZksLgRM/TZlKnousKH9bbTazUWRRw== 1074 | dependencies: 1075 | "@types/strip-bom" "^3.0.0" 1076 | "@types/strip-json-comments" "0.0.30" 1077 | strip-bom "^3.0.0" 1078 | strip-json-comments "^2.0.0" 1079 | 1080 | type-is@~1.6.18: 1081 | version "1.6.18" 1082 | resolved "https://registry.yarnpkg.com/type-is/-/type-is-1.6.18.tgz#4e552cd05df09467dcbc4ef739de89f2cf37c131" 1083 | integrity sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g== 1084 | dependencies: 1085 | media-typer "0.3.0" 1086 | mime-types "~2.1.24" 1087 | 1088 | typescript@^5.4.5: 1089 | version "5.5.3" 1090 | resolved "https://registry.yarnpkg.com/typescript/-/typescript-5.5.3.tgz#e1b0a3c394190838a0b168e771b0ad56a0af0faa" 1091 | integrity sha512-/hreyEujaB0w76zKo6717l3L0o/qEUtRgdvUBvlkhoWeOVMjMuHNHk0BRBzikzuGDqNmPQbg5ifMEqsHLiIUcQ== 1092 | 1093 | undici-types@~5.26.4: 1094 | version "5.26.5" 1095 | resolved "https://registry.yarnpkg.com/undici-types/-/undici-types-5.26.5.tgz#bcd539893d00b56e964fd2657a4866b221a65617" 1096 | integrity sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA== 1097 | 1098 | universalify@^2.0.0: 1099 | version "2.0.1" 1100 | resolved "https://registry.yarnpkg.com/universalify/-/universalify-2.0.1.tgz#168efc2180964e6386d061e094df61afe239b18d" 1101 | integrity sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw== 1102 | 1103 | unpipe@1.0.0, unpipe@~1.0.0: 1104 | version "1.0.0" 1105 | resolved "https://registry.yarnpkg.com/unpipe/-/unpipe-1.0.0.tgz#b2bf4ee8514aae6165b4817829d21b2ef49904ec" 1106 | integrity sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ== 1107 | 1108 | utils-merge@1.0.1: 1109 | version "1.0.1" 1110 | resolved "https://registry.yarnpkg.com/utils-merge/-/utils-merge-1.0.1.tgz#9f95710f50a267947b2ccc124741c1028427e713" 1111 | integrity sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA== 1112 | 1113 | v8-compile-cache-lib@^3.0.1: 1114 | version "3.0.1" 1115 | resolved "https://registry.yarnpkg.com/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz#6336e8d71965cb3d35a1bbb7868445a7c05264bf" 1116 | integrity sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg== 1117 | 1118 | vary@~1.1.2: 1119 | version "1.1.2" 1120 | resolved "https://registry.yarnpkg.com/vary/-/vary-1.1.2.tgz#2299f02c6ded30d4a5961b0b9f74524a18f634fc" 1121 | integrity sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg== 1122 | 1123 | wrappy@1: 1124 | version "1.0.2" 1125 | resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f" 1126 | integrity sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ== 1127 | 1128 | xtend@^4.0.0: 1129 | version "4.0.2" 1130 | resolved "https://registry.yarnpkg.com/xtend/-/xtend-4.0.2.tgz#bb72779f5fa465186b1f438f674fa347fdb5db54" 1131 | integrity sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ== 1132 | 1133 | yn@3.1.1: 1134 | version "3.1.1" 1135 | resolved "https://registry.yarnpkg.com/yn/-/yn-3.1.1.tgz#1e87401a09d767c1d5eab26a6e4c185182d2eb50" 1136 | integrity sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q== 1137 | --------------------------------------------------------------------------------