├── src ├── index.ts ├── types.d.ts ├── web.ts └── db │ ├── client.ts │ ├── server.ts │ └── rawclient.ts ├── package.json ├── tsconfig.json ├── LICENSE ├── .gitignore ├── README.md └── yarn.lock /src/index.ts: -------------------------------------------------------------------------------- 1 | import { Client } from "./db/client"; 2 | import { RawClient } from "./db/rawclient"; 3 | 4 | export { Client } from "./db/client"; 5 | export { RawClient } from "./db/rawclient"; 6 | export { exposeRead, exposeReadWrite, exposeWrite } from "./db/server"; 7 | export { AUTH_SNIPPET, requireAuth } from "./web"; 8 | 9 | export const rawDB = new RawClient(process.env.REPLIT_DB_URL || ""); 10 | export const db = Client.create(process.env.REPLIT_DB_URL || ""); 11 | -------------------------------------------------------------------------------- /src/types.d.ts: -------------------------------------------------------------------------------- 1 | import * as express from "express"; 2 | 3 | type RequestAuthContext = { name: string }; 4 | 5 | declare global { 6 | declare namespace Express { 7 | export interface Request { 8 | auth?: RequestAuthContext; 9 | } 10 | } 11 | } 12 | 13 | export type Middleware = express.RequestHandler; 14 | 15 | export interface IDatabaseClient { 16 | get(key: string): Promise; 17 | setMany(items: Map): Promise; 18 | list(prefix: string): Promise; 19 | delete(key: string): Promise; 20 | } 21 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "replit-node", 3 | "version": "0.0.1", 4 | "description": "A library for creating things inside replit repls", 5 | "main": "src/index.ts", 6 | "repository": "https://github.com/replit/replit-node", 7 | "author": "Spencer Pogorzelski <34356756+Scoder12@users.noreply.github.com>", 8 | "license": "MIT", 9 | "scripts": { 10 | "dev": "tsc -w" 11 | }, 12 | "devDependencies": { 13 | "@types/express": "^4.17.13", 14 | "@types/node": "^16.11.6", 15 | "typescript": "^4.4.4" 16 | }, 17 | "dependencies": { 18 | "undici": "^4.9.5" 19 | }, 20 | "peerDependencies": { 21 | "express": "^4.17.1" 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es2017", 4 | "module": "commonjs", 5 | "lib": ["dom", "es6", "es2017", "esnext.asynciterable"], 6 | "skipLibCheck": true, 7 | "sourceMap": true, 8 | "outDir": "./dist", 9 | "moduleResolution": "node", 10 | "removeComments": true, 11 | "noImplicitAny": true, 12 | "strictNullChecks": true, 13 | "strictFunctionTypes": true, 14 | "noImplicitThis": true, 15 | "noUnusedLocals": true, 16 | "noUnusedParameters": true, 17 | "noImplicitReturns": true, 18 | "noFallthroughCasesInSwitch": true, 19 | "allowSyntheticDefaultImports": true, 20 | "esModuleInterop": true, 21 | "emitDecoratorMetadata": true, 22 | "experimentalDecorators": true, 23 | "resolveJsonModule": true, 24 | "baseUrl": "." 25 | }, 26 | "exclude": ["node_modules"], 27 | "include": ["./src/**/*.ts"] 28 | } 29 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 Replit 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /src/web.ts: -------------------------------------------------------------------------------- 1 | import * as express from "express"; 2 | import { Middleware, RequestAuthContext } from "./types"; 3 | 4 | export function headerProxy( 5 | req: express.Request, 6 | obj: Record, 7 | prop: string, 8 | headerName: string 9 | ): void { 10 | Object.defineProperty(obj, prop, { 11 | configurable: false, 12 | enumerable: true, 13 | get: function () { 14 | return req.headers[headerName]; 15 | }, 16 | }); 17 | } 18 | 19 | export const authMiddleware: Middleware = (req, _res, next) => { 20 | if (req.headers["x-replit-user-id"]) { 21 | const authObj = {}; 22 | headerProxy(req, authObj, "id", "x-replit-user-id"); 23 | headerProxy(req, authObj, "name", "x-replit-user-name"); 24 | headerProxy(req, authObj, "roles", "x-replit-user-roles"); 25 | req.auth = authObj as RequestAuthContext; 26 | } 27 | return next(); 28 | }; 29 | 30 | export const AUTH_SNIPPET = 31 | "'; 34 | 35 | export function requireAuth(loginRes: string = AUTH_SNIPPET): Middleware { 36 | return (req, res, next) => { 37 | if (req.auth) return next(); 38 | res.send(loginRes); 39 | }; 40 | } 41 | -------------------------------------------------------------------------------- /src/db/client.ts: -------------------------------------------------------------------------------- 1 | import { IDatabaseClient } from "../types"; 2 | import { RawClient } from "./rawclient"; 3 | 4 | export class Client implements IDatabaseClient { 5 | readonly rawClient: RawClient; 6 | readonly cache: Map; 7 | 8 | constructor(rawClient: RawClient, cache: Map) { 9 | this.rawClient = rawClient; 10 | this.cache = cache; 11 | } 12 | 13 | static create(url: string) { 14 | return new Client(new RawClient(url), new Map()); 15 | } 16 | 17 | emptyCache() { 18 | this.cache.clear(); 19 | } 20 | 21 | async get(key: string): Promise { 22 | if (this.cache.has(key)) { 23 | return this.cache.get(key) as string; 24 | } 25 | const value = await this.rawClient.get(key); 26 | this.cache.set(key, value); 27 | return value; 28 | } 29 | 30 | async setMany(items: Map) { 31 | await this.rawClient.setMany(items); 32 | for (const [key, value] of items.entries()) { 33 | this.cache.set(key, value); 34 | } 35 | } 36 | 37 | async set(key: string, value: string) { 38 | await this.setMany(new Map([[key, value]])); 39 | } 40 | 41 | async list(prefix: string) { 42 | // caching this would require pulling the entire DB down at startup, and being 43 | // forced to create the DB Client in an async context is no fun. Implementing 44 | // such a cache is left as an exercise to the reader. 45 | return this.rawClient.list(prefix); 46 | } 47 | 48 | async delete(key: string) { 49 | await this.rawClient.delete(key); 50 | this.cache.delete(key); 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | lerna-debug.log* 8 | 9 | # Diagnostic reports (https://nodejs.org/api/report.html) 10 | report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json 11 | 12 | # Runtime data 13 | pids 14 | *.pid 15 | *.seed 16 | *.pid.lock 17 | 18 | # Directory for instrumented libs generated by jscoverage/JSCover 19 | lib-cov 20 | 21 | # Coverage directory used by tools like istanbul 22 | coverage 23 | *.lcov 24 | 25 | # nyc test coverage 26 | .nyc_output 27 | 28 | # Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files) 29 | .grunt 30 | 31 | # Bower dependency directory (https://bower.io/) 32 | bower_components 33 | 34 | # node-waf configuration 35 | .lock-wscript 36 | 37 | # Compiled binary addons (https://nodejs.org/api/addons.html) 38 | build/Release 39 | 40 | # Dependency directories 41 | node_modules/ 42 | jspm_packages/ 43 | 44 | # TypeScript v1 declaration files 45 | typings/ 46 | 47 | # TypeScript cache 48 | *.tsbuildinfo 49 | 50 | # Optional npm cache directory 51 | .npm 52 | 53 | # Optional eslint cache 54 | .eslintcache 55 | 56 | # Microbundle cache 57 | .rpt2_cache/ 58 | .rts2_cache_cjs/ 59 | .rts2_cache_es/ 60 | .rts2_cache_umd/ 61 | 62 | # Optional REPL history 63 | .node_repl_history 64 | 65 | # Output of 'npm pack' 66 | *.tgz 67 | 68 | # Yarn Integrity file 69 | .yarn-integrity 70 | 71 | # dotenv environment variables file 72 | .env 73 | .env.test 74 | 75 | # parcel-bundler cache (https://parceljs.org/) 76 | .cache 77 | 78 | # Next.js build output 79 | .next 80 | 81 | # Nuxt.js build / generate output 82 | .nuxt 83 | dist 84 | 85 | # Gatsby files 86 | .cache/ 87 | # Comment in the public line in if your project uses Gatsby and *not* Next.js 88 | # https://nextjs.org/blog/next-9-1#public-directory-support 89 | # public 90 | 91 | # vuepress build output 92 | .vuepress/dist 93 | 94 | # Serverless directories 95 | .serverless/ 96 | 97 | # FuseBox cache 98 | .fusebox/ 99 | 100 | # DynamoDB Local files 101 | .dynamodb/ 102 | 103 | # TernJS port file 104 | .tern-port 105 | -------------------------------------------------------------------------------- /src/db/server.ts: -------------------------------------------------------------------------------- 1 | import { json as expressJSON, Router } from "express"; 2 | import { IDatabaseClient, Middleware } from "../types"; 3 | import { KeyError } from "./rawclient"; 4 | 5 | // Handles async errors by passing them to the error handler middleware. 6 | // Without this, the request will simply hang indefinitely on error. 7 | // express 5.0 does this out of the box but we want to support old versions 8 | function wrapAsync(func: Middleware): Middleware { 9 | return async (req, res, next) => { 10 | try { 11 | return await func(req, res, next); 12 | } catch (e) { 13 | return next(e); 14 | } 15 | }; 16 | } 17 | 18 | export function exposeRead(db: IDatabaseClient, ...keys: string[]): Middleware { 19 | const router = Router(); 20 | const allowed = new Set(keys); 21 | router.get( 22 | "/:key", 23 | wrapAsync(async (req, res, next) => { 24 | const { key } = req.params; 25 | if (!allowed.has(key)) return next(); 26 | 27 | let value; 28 | try { 29 | value = await db.get(key); 30 | } catch (e) { 31 | if (e instanceof KeyError) { 32 | return res.status(404).json({ error: "Key not found" }); 33 | } 34 | throw e; 35 | } 36 | 37 | return res.json({ value }); 38 | }) 39 | ); 40 | return router; 41 | } 42 | 43 | export function exposeWrite( 44 | db: IDatabaseClient, 45 | ...keys: string[] 46 | ): Middleware { 47 | const router = Router(); 48 | const allowed = new Set(keys); 49 | router.post( 50 | "/:key", 51 | expressJSON(), 52 | wrapAsync(async (req, res, next) => { 53 | const { key } = req.params; 54 | if (!allowed.has(key)) return next(); 55 | 56 | const val = req.body.value; 57 | if (typeof val !== "string") { 58 | return res.status(400).json({ error: "Missing value in request body" }); 59 | } 60 | 61 | await db.setMany(new Map([[key, val]])); 62 | return res.json({ ok: true }); 63 | }) 64 | ); 65 | return router; 66 | } 67 | 68 | export function exposeReadWrite( 69 | db: IDatabaseClient, 70 | ...keys: string[] 71 | ): Middleware { 72 | const router = Router(); 73 | router.use(exposeRead(db, ...keys)); 74 | router.use(exposeWrite(db, ...keys)); 75 | return router; 76 | } 77 | -------------------------------------------------------------------------------- /src/db/rawclient.ts: -------------------------------------------------------------------------------- 1 | import { request } from "undici"; 2 | import { IDatabaseClient } from "../types"; 3 | 4 | export class KeyError extends Error {} 5 | 6 | /** Raw, low-level API access. */ 7 | export class RawClient implements IDatabaseClient { 8 | /** URL should not have a trailing slash, but it doesn't really matter. */ 9 | readonly url: string; 10 | 11 | constructor(url: string) { 12 | this.url = url; 13 | } 14 | 15 | async get(key: string): Promise { 16 | const { statusCode, body } = await request( 17 | this.url + "/" + encodeURIComponent(key) 18 | ); 19 | if (statusCode == 404) { 20 | throw new KeyError("Key not found"); 21 | } 22 | if (statusCode != 200) { 23 | throw new Error( 24 | `Unexpected status code while getting key: ${statusCode}` 25 | ); 26 | } 27 | return body.text(); 28 | } 29 | 30 | async setMany(items: Map): Promise { 31 | const requestBodyItems = []; 32 | for (const [key, val] of items.entries()) { 33 | requestBodyItems.push( 34 | encodeURIComponent(key) + "=" + encodeURIComponent(val) 35 | ); 36 | } 37 | const { statusCode } = await request(this.url, { 38 | method: "POST", 39 | body: requestBodyItems.join("&"), 40 | headers: { "Content-Type": "application/x-www-form-urlencoded" }, 41 | }); 42 | if (statusCode != 200) { 43 | throw new Error( 44 | `Unexpected status code while setting keys: ${statusCode}` 45 | ); 46 | } 47 | } 48 | 49 | async delete(key: string): Promise { 50 | const { statusCode } = await request( 51 | this.url + "/" + encodeURIComponent(key), 52 | { method: "DELETE" } 53 | ); 54 | if (statusCode != 204) { 55 | throw new Error( 56 | `Unexpected status code while deleting key: ${statusCode}` 57 | ); 58 | } 59 | } 60 | 61 | async list(prefix: string): Promise { 62 | const { statusCode, body } = await request( 63 | this.url + "?encode=true&prefix=" + encodeURIComponent(prefix) 64 | ); 65 | if (statusCode != 200) { 66 | throw new Error( 67 | `Unexpected status code while listing keys: ${statusCode}` 68 | ); 69 | } 70 | return (await body.text()) 71 | .split("\n") 72 | .map((val) => decodeURIComponent(val)); 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # replit-node 2 | 3 | A node.js library that helps you build excellent things inside Repls! 4 | 5 | ## Features 6 | 7 | ### Auth Middleware 8 | 9 | The auth middleware allows for simple use of 10 | [Replit Auth](https://docs.replit.com/hosting/authenticating-users-repl-auth) by 11 | creating a `req.auth` object with the user's `id`, `name`, and `roles`. When the user 12 | is not signed in, `req.auth` is `undefined`. 13 | 14 | You can activate the middleware for you entire app like this: 15 | 16 | ```js 17 | const replit = require("PKG_NAME_TBD"); 18 | const express = require("express"); 19 | 20 | const app = express(); 21 | app.use(replit.authMiddleware); 22 | ``` 23 | 24 | You can then use the `req.auth` object in your routes: 25 | 26 | ```js 27 | app.get("/", (req, res) => { 28 | // for demo purposes. it's shorter to use `requireAuth` instead of this. 29 | if (req.auth) { 30 | res.end(`Hello, ${req.auth.name}`); 31 | } else { 32 | res.send(`Sign in with Repl Auth to use this demo: ${replit.AUTH_SNIPPET}`); 33 | } 34 | }); 35 | ``` 36 | 37 | ### requireAuth Middleware 38 | 39 | The `requireAuth` middleware simplifies showing a login screen whenever the user isn't 40 | signed in with Replit auth. By default, it returns the `AUTH_SNIPPET` whenever the user 41 | isn't signed in. For example: 42 | 43 | ```js 44 | app.get("/", replit.requireAuth(), (req, res) => 45 | res.end(`Hello, ${req.auth.name}`) 46 | ); 47 | ``` 48 | 49 | You can also customize the login response: 50 | 51 | ```js 52 | app.get( 53 | "/", 54 | replit.requireAuth( 55 | `Sign in with Repl Auth to use this demo: ${replit.AUTH_SNIPPET}` 56 | ), 57 | (req, res) => res.end(`Hello, ${req.auth.name}`) 58 | ); 59 | ``` 60 | 61 | #### Note about AUTH_SNIPPET 62 | 63 | The auth snippet includes some JavaScript that generates a button that users can click 64 | to sign in to your app with Replit auth. Once they are signed in, it will reload the 65 | page. This means that using it will only work on pages where the user is directly 66 | visiting user their browser and a GET request. It won't work with API routes. In 67 | certain cases, it may make more sense to redirect users to a dedicated login page 68 | instead. You can access the auth snippet by including `replit.AUTH_SNIPPET` in an HTML 69 | response (`res.send()`). 70 | 71 | ### Raw Database Client 72 | 73 | For predictable results, use the `RawClient`. It is nothing but a node interface to the 74 | database API without any transformations or niceties. You can use it like this: 75 | 76 | ```js 77 | // initialize it yourself 78 | const db = new replit.RawClient(process.env.REPLIT_DB_URL); 79 | // or use the shorthand 80 | replit.rawDB; 81 | ``` 82 | 83 | You can then call the `db.list`, `db.get`, `db.setMany`, and `db.delete` functions. 84 | 85 | ### Cached Database Client 86 | 87 | The database client caches all reads and writes performed after the client was created. 88 | It is not safe to use if multiple threads or processes are writing to the database at 89 | the same time, as this can lead to incorrect results being returned from the cache. You 90 | can use it like this: 91 | 92 | ```js 93 | const db = new replit.Client.create(process.env.REPLIT_DB_URL); 94 | // or 95 | replit.db; 96 | ``` 97 | 98 | You can use all the same methods the raw client in addition to `db.set` which uses 99 | `setMany` under the hood. 100 | -------------------------------------------------------------------------------- /yarn.lock: -------------------------------------------------------------------------------- 1 | # THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. 2 | # yarn lockfile v1 3 | 4 | 5 | "@types/body-parser@*": 6 | version "1.19.1" 7 | resolved "https://registry.yarnpkg.com/@types/body-parser/-/body-parser-1.19.1.tgz#0c0174c42a7d017b818303d4b5d969cb0b75929c" 8 | integrity sha512-a6bTJ21vFOGIkwM0kzh9Yr89ziVxq4vYH2fQ6N8AeipEzai/cFK6aGMArIkUeIdRIgpwQa+2bXiLuUJCpSf2Cg== 9 | dependencies: 10 | "@types/connect" "*" 11 | "@types/node" "*" 12 | 13 | "@types/connect@*": 14 | version "3.4.35" 15 | resolved "https://registry.yarnpkg.com/@types/connect/-/connect-3.4.35.tgz#5fcf6ae445e4021d1fc2219a4873cc73a3bb2ad1" 16 | integrity sha512-cdeYyv4KWoEgpBISTxWvqYsVy444DOqehiF3fM3ne10AmJ62RSyNkUnxMJXHQWRQQX2eR94m5y1IZyDwBjV9FQ== 17 | dependencies: 18 | "@types/node" "*" 19 | 20 | "@types/express-serve-static-core@^4.17.18": 21 | version "4.17.24" 22 | resolved "https://registry.yarnpkg.com/@types/express-serve-static-core/-/express-serve-static-core-4.17.24.tgz#ea41f93bf7e0d59cd5a76665068ed6aab6815c07" 23 | integrity sha512-3UJuW+Qxhzwjq3xhwXm2onQcFHn76frIYVbTu+kn24LFxI+dEhdfISDFovPB8VpEgW8oQCTpRuCe+0zJxB7NEA== 24 | dependencies: 25 | "@types/node" "*" 26 | "@types/qs" "*" 27 | "@types/range-parser" "*" 28 | 29 | "@types/express@^4.17.13": 30 | version "4.17.13" 31 | resolved "https://registry.yarnpkg.com/@types/express/-/express-4.17.13.tgz#a76e2995728999bab51a33fabce1d705a3709034" 32 | integrity sha512-6bSZTPaTIACxn48l50SR+axgrqm6qXFIxrdAKaG6PaJk3+zuUr35hBlgT7vOmJcum+OEaIBLtHV/qloEAFITeA== 33 | dependencies: 34 | "@types/body-parser" "*" 35 | "@types/express-serve-static-core" "^4.17.18" 36 | "@types/qs" "*" 37 | "@types/serve-static" "*" 38 | 39 | "@types/mime@^1": 40 | version "1.3.2" 41 | resolved "https://registry.yarnpkg.com/@types/mime/-/mime-1.3.2.tgz#93e25bf9ee75fe0fd80b594bc4feb0e862111b5a" 42 | integrity sha512-YATxVxgRqNH6nHEIsvg6k2Boc1JHI9ZbH5iWFFv/MTkchz3b1ieGDa5T0a9RznNdI0KhVbdbWSN+KWWrQZRxTw== 43 | 44 | "@types/node@*", "@types/node@^16.11.6": 45 | version "16.11.6" 46 | resolved "https://registry.yarnpkg.com/@types/node/-/node-16.11.6.tgz#6bef7a2a0ad684cf6e90fcfe31cecabd9ce0a3ae" 47 | integrity sha512-ua7PgUoeQFjmWPcoo9khiPum3Pd60k4/2ZGXt18sm2Slk0W0xZTqt5Y0Ny1NyBiN1EVQ/+FaF9NcY4Qe6rwk5w== 48 | 49 | "@types/qs@*": 50 | version "6.9.7" 51 | resolved "https://registry.yarnpkg.com/@types/qs/-/qs-6.9.7.tgz#63bb7d067db107cc1e457c303bc25d511febf6cb" 52 | integrity sha512-FGa1F62FT09qcrueBA6qYTrJPVDzah9a+493+o2PCXsesWHIn27G98TsSMs3WPNbZIEj4+VJf6saSFpvD+3Zsw== 53 | 54 | "@types/range-parser@*": 55 | version "1.2.4" 56 | resolved "https://registry.yarnpkg.com/@types/range-parser/-/range-parser-1.2.4.tgz#cd667bcfdd025213aafb7ca5915a932590acdcdc" 57 | integrity sha512-EEhsLsD6UsDM1yFhAvy0Cjr6VwmpMWqFBCb9w07wVugF7w9nfajxLuVmngTIpgS6svCnm6Vaw+MZhoDCKnOfsw== 58 | 59 | "@types/serve-static@*": 60 | version "1.13.10" 61 | resolved "https://registry.yarnpkg.com/@types/serve-static/-/serve-static-1.13.10.tgz#f5e0ce8797d2d7cc5ebeda48a52c96c4fa47a8d9" 62 | integrity sha512-nCkHGI4w7ZgAdNkrEu0bv+4xNV/XDqW+DydknebMOQwkpDGx8G+HTlj7R7ABI8i8nKxVw0wtKPi1D+lPOkh4YQ== 63 | dependencies: 64 | "@types/mime" "^1" 65 | "@types/node" "*" 66 | 67 | accepts@~1.3.7: 68 | version "1.3.7" 69 | resolved "https://registry.yarnpkg.com/accepts/-/accepts-1.3.7.tgz#531bc726517a3b2b41f850021c6cc15eaab507cd" 70 | integrity sha512-Il80Qs2WjYlJIBNzNkK6KYqlVMTbZLXgHx2oT0pU/fjRHyEp+PEfEPY0R3WCwAGVOtauxh1hOxNgIf5bv7dQpA== 71 | dependencies: 72 | mime-types "~2.1.24" 73 | negotiator "0.6.2" 74 | 75 | array-flatten@1.1.1: 76 | version "1.1.1" 77 | resolved "https://registry.yarnpkg.com/array-flatten/-/array-flatten-1.1.1.tgz#9a5f699051b1e7073328f2a008968b64ea2955d2" 78 | integrity sha1-ml9pkFGx5wczKPKgCJaLZOopVdI= 79 | 80 | body-parser@1.19.0: 81 | version "1.19.0" 82 | resolved "https://registry.yarnpkg.com/body-parser/-/body-parser-1.19.0.tgz#96b2709e57c9c4e09a6fd66a8fd979844f69f08a" 83 | integrity sha512-dhEPs72UPbDnAQJ9ZKMNTP6ptJaionhP5cBb541nXPlW60Jepo9RV/a4fX4XWW9CuFNK22krhrj1+rgzifNCsw== 84 | dependencies: 85 | bytes "3.1.0" 86 | content-type "~1.0.4" 87 | debug "2.6.9" 88 | depd "~1.1.2" 89 | http-errors "1.7.2" 90 | iconv-lite "0.4.24" 91 | on-finished "~2.3.0" 92 | qs "6.7.0" 93 | raw-body "2.4.0" 94 | type-is "~1.6.17" 95 | 96 | bytes@3.1.0: 97 | version "3.1.0" 98 | resolved "https://registry.yarnpkg.com/bytes/-/bytes-3.1.0.tgz#f6cf7933a360e0588fa9fde85651cdc7f805d1f6" 99 | integrity sha512-zauLjrfCG+xvoyaqLoV8bLVXXNGC4JqlxFCutSDWA6fJrTo2ZuvLYTqZ7aHBLZSMOopbzwv8f+wZcVzfVTI2Dg== 100 | 101 | content-disposition@0.5.3: 102 | version "0.5.3" 103 | resolved "https://registry.yarnpkg.com/content-disposition/-/content-disposition-0.5.3.tgz#e130caf7e7279087c5616c2007d0485698984fbd" 104 | integrity sha512-ExO0774ikEObIAEV9kDo50o+79VCUdEB6n6lzKgGwupcVeRlhrj3qGAfwq8G6uBJjkqLrhT0qEYFcWng8z1z0g== 105 | dependencies: 106 | safe-buffer "5.1.2" 107 | 108 | content-type@~1.0.4: 109 | version "1.0.4" 110 | resolved "https://registry.yarnpkg.com/content-type/-/content-type-1.0.4.tgz#e138cc75e040c727b1966fe5e5f8c9aee256fe3b" 111 | integrity sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA== 112 | 113 | cookie-signature@1.0.6: 114 | version "1.0.6" 115 | resolved "https://registry.yarnpkg.com/cookie-signature/-/cookie-signature-1.0.6.tgz#e303a882b342cc3ee8ca513a79999734dab3ae2c" 116 | integrity sha1-4wOogrNCzD7oylE6eZmXNNqzriw= 117 | 118 | cookie@0.4.0: 119 | version "0.4.0" 120 | resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.4.0.tgz#beb437e7022b3b6d49019d088665303ebe9c14ba" 121 | integrity sha512-+Hp8fLp57wnUSt0tY0tHEXh4voZRDnoIrZPqlo3DPiI4y9lwg/jqx+1Om94/W6ZaPDOUbnjOt/99w66zk+l1Xg== 122 | 123 | debug@2.6.9: 124 | version "2.6.9" 125 | resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f" 126 | integrity sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA== 127 | dependencies: 128 | ms "2.0.0" 129 | 130 | depd@~1.1.2: 131 | version "1.1.2" 132 | resolved "https://registry.yarnpkg.com/depd/-/depd-1.1.2.tgz#9bcd52e14c097763e749b274c4346ed2e560b5a9" 133 | integrity sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak= 134 | 135 | destroy@~1.0.4: 136 | version "1.0.4" 137 | resolved "https://registry.yarnpkg.com/destroy/-/destroy-1.0.4.tgz#978857442c44749e4206613e37946205826abd80" 138 | integrity sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA= 139 | 140 | ee-first@1.1.1: 141 | version "1.1.1" 142 | resolved "https://registry.yarnpkg.com/ee-first/-/ee-first-1.1.1.tgz#590c61156b0ae2f4f0255732a158b266bc56b21d" 143 | integrity sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0= 144 | 145 | encodeurl@~1.0.2: 146 | version "1.0.2" 147 | resolved "https://registry.yarnpkg.com/encodeurl/-/encodeurl-1.0.2.tgz#ad3ff4c86ec2d029322f5a02c3a9a606c95b3f59" 148 | integrity sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k= 149 | 150 | escape-html@~1.0.3: 151 | version "1.0.3" 152 | resolved "https://registry.yarnpkg.com/escape-html/-/escape-html-1.0.3.tgz#0258eae4d3d0c0974de1c169188ef0051d1d1988" 153 | integrity sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg= 154 | 155 | etag@~1.8.1: 156 | version "1.8.1" 157 | resolved "https://registry.yarnpkg.com/etag/-/etag-1.8.1.tgz#41ae2eeb65efa62268aebfea83ac7d79299b0887" 158 | integrity sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc= 159 | 160 | express@^4.17.1: 161 | version "4.17.1" 162 | resolved "https://registry.yarnpkg.com/express/-/express-4.17.1.tgz#4491fc38605cf51f8629d39c2b5d026f98a4c134" 163 | integrity sha512-mHJ9O79RqluphRrcw2X/GTh3k9tVv8YcoyY4Kkh4WDMUYKRZUq0h1o0w2rrrxBqM7VoeUVqgb27xlEMXTnYt4g== 164 | dependencies: 165 | accepts "~1.3.7" 166 | array-flatten "1.1.1" 167 | body-parser "1.19.0" 168 | content-disposition "0.5.3" 169 | content-type "~1.0.4" 170 | cookie "0.4.0" 171 | cookie-signature "1.0.6" 172 | debug "2.6.9" 173 | depd "~1.1.2" 174 | encodeurl "~1.0.2" 175 | escape-html "~1.0.3" 176 | etag "~1.8.1" 177 | finalhandler "~1.1.2" 178 | fresh "0.5.2" 179 | merge-descriptors "1.0.1" 180 | methods "~1.1.2" 181 | on-finished "~2.3.0" 182 | parseurl "~1.3.3" 183 | path-to-regexp "0.1.7" 184 | proxy-addr "~2.0.5" 185 | qs "6.7.0" 186 | range-parser "~1.2.1" 187 | safe-buffer "5.1.2" 188 | send "0.17.1" 189 | serve-static "1.14.1" 190 | setprototypeof "1.1.1" 191 | statuses "~1.5.0" 192 | type-is "~1.6.18" 193 | utils-merge "1.0.1" 194 | vary "~1.1.2" 195 | 196 | finalhandler@~1.1.2: 197 | version "1.1.2" 198 | resolved "https://registry.yarnpkg.com/finalhandler/-/finalhandler-1.1.2.tgz#b7e7d000ffd11938d0fdb053506f6ebabe9f587d" 199 | integrity sha512-aAWcW57uxVNrQZqFXjITpW3sIUQmHGG3qSb9mUah9MgMC4NeWhNOlNjXEYq3HjRAvL6arUviZGGJsBg6z0zsWA== 200 | dependencies: 201 | debug "2.6.9" 202 | encodeurl "~1.0.2" 203 | escape-html "~1.0.3" 204 | on-finished "~2.3.0" 205 | parseurl "~1.3.3" 206 | statuses "~1.5.0" 207 | unpipe "~1.0.0" 208 | 209 | forwarded@0.2.0: 210 | version "0.2.0" 211 | resolved "https://registry.yarnpkg.com/forwarded/-/forwarded-0.2.0.tgz#2269936428aad4c15c7ebe9779a84bf0b2a81811" 212 | integrity sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow== 213 | 214 | fresh@0.5.2: 215 | version "0.5.2" 216 | resolved "https://registry.yarnpkg.com/fresh/-/fresh-0.5.2.tgz#3d8cadd90d976569fa835ab1f8e4b23a105605a7" 217 | integrity sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac= 218 | 219 | http-errors@1.7.2: 220 | version "1.7.2" 221 | resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-1.7.2.tgz#4f5029cf13239f31036e5b2e55292bcfbcc85c8f" 222 | integrity sha512-uUQBt3H/cSIVfch6i1EuPNy/YsRSOUBXTVfZ+yR7Zjez3qjBz6i9+i4zjNaoqcoFVI4lQJ5plg63TvGfRSDCRg== 223 | dependencies: 224 | depd "~1.1.2" 225 | inherits "2.0.3" 226 | setprototypeof "1.1.1" 227 | statuses ">= 1.5.0 < 2" 228 | toidentifier "1.0.0" 229 | 230 | http-errors@~1.7.2: 231 | version "1.7.3" 232 | resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-1.7.3.tgz#6c619e4f9c60308c38519498c14fbb10aacebb06" 233 | integrity sha512-ZTTX0MWrsQ2ZAhA1cejAwDLycFsd7I7nVtnkT3Ol0aqodaKW+0CTZDQ1uBv5whptCnc8e8HeRRJxRs0kmm/Qfw== 234 | dependencies: 235 | depd "~1.1.2" 236 | inherits "2.0.4" 237 | setprototypeof "1.1.1" 238 | statuses ">= 1.5.0 < 2" 239 | toidentifier "1.0.0" 240 | 241 | iconv-lite@0.4.24: 242 | version "0.4.24" 243 | resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.24.tgz#2022b4b25fbddc21d2f524974a474aafe733908b" 244 | integrity sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA== 245 | dependencies: 246 | safer-buffer ">= 2.1.2 < 3" 247 | 248 | inherits@2.0.3: 249 | version "2.0.3" 250 | resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.3.tgz#633c2c83e3da42a502f52466022480f4208261de" 251 | integrity sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4= 252 | 253 | inherits@2.0.4: 254 | version "2.0.4" 255 | resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c" 256 | integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== 257 | 258 | ipaddr.js@1.9.1: 259 | version "1.9.1" 260 | resolved "https://registry.yarnpkg.com/ipaddr.js/-/ipaddr.js-1.9.1.tgz#bff38543eeb8984825079ff3a2a8e6cbd46781b3" 261 | integrity sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g== 262 | 263 | media-typer@0.3.0: 264 | version "0.3.0" 265 | resolved "https://registry.yarnpkg.com/media-typer/-/media-typer-0.3.0.tgz#8710d7af0aa626f8fffa1ce00168545263255748" 266 | integrity sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g= 267 | 268 | merge-descriptors@1.0.1: 269 | version "1.0.1" 270 | resolved "https://registry.yarnpkg.com/merge-descriptors/-/merge-descriptors-1.0.1.tgz#b00aaa556dd8b44568150ec9d1b953f3f90cbb61" 271 | integrity sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E= 272 | 273 | methods@~1.1.2: 274 | version "1.1.2" 275 | resolved "https://registry.yarnpkg.com/methods/-/methods-1.1.2.tgz#5529a4d67654134edcc5266656835b0f851afcee" 276 | integrity sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4= 277 | 278 | mime-db@1.50.0: 279 | version "1.50.0" 280 | resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.50.0.tgz#abd4ac94e98d3c0e185016c67ab45d5fde40c11f" 281 | integrity sha512-9tMZCDlYHqeERXEHO9f/hKfNXhre5dK2eE/krIvUjZbS2KPcqGDfNShIWS1uW9XOTKQKqK6qbeOci18rbfW77A== 282 | 283 | mime-types@~2.1.24: 284 | version "2.1.33" 285 | resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.33.tgz#1fa12a904472fafd068e48d9e8401f74d3f70edb" 286 | integrity sha512-plLElXp7pRDd0bNZHw+nMd52vRYjLwQjygaNg7ddJ2uJtTlmnTCjWuPKxVu6//AdaRuME84SvLW91sIkBqGT0g== 287 | dependencies: 288 | mime-db "1.50.0" 289 | 290 | mime@1.6.0: 291 | version "1.6.0" 292 | resolved "https://registry.yarnpkg.com/mime/-/mime-1.6.0.tgz#32cd9e5c64553bd58d19a568af452acff04981b1" 293 | integrity sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg== 294 | 295 | ms@2.0.0: 296 | version "2.0.0" 297 | resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8" 298 | integrity sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g= 299 | 300 | ms@2.1.1: 301 | version "2.1.1" 302 | resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.1.tgz#30a5864eb3ebb0a66f2ebe6d727af06a09d86e0a" 303 | integrity sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg== 304 | 305 | negotiator@0.6.2: 306 | version "0.6.2" 307 | resolved "https://registry.yarnpkg.com/negotiator/-/negotiator-0.6.2.tgz#feacf7ccf525a77ae9634436a64883ffeca346fb" 308 | integrity sha512-hZXc7K2e+PgeI1eDBe/10Ard4ekbfrrqG8Ep+8Jmf4JID2bNg7NvCPOZN+kfF574pFQI7mum2AUqDidoKqcTOw== 309 | 310 | on-finished@~2.3.0: 311 | version "2.3.0" 312 | resolved "https://registry.yarnpkg.com/on-finished/-/on-finished-2.3.0.tgz#20f1336481b083cd75337992a16971aa2d906947" 313 | integrity sha1-IPEzZIGwg811M3mSoWlxqi2QaUc= 314 | dependencies: 315 | ee-first "1.1.1" 316 | 317 | parseurl@~1.3.3: 318 | version "1.3.3" 319 | resolved "https://registry.yarnpkg.com/parseurl/-/parseurl-1.3.3.tgz#9da19e7bee8d12dff0513ed5b76957793bc2e8d4" 320 | integrity sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ== 321 | 322 | path-to-regexp@0.1.7: 323 | version "0.1.7" 324 | resolved "https://registry.yarnpkg.com/path-to-regexp/-/path-to-regexp-0.1.7.tgz#df604178005f522f15eb4490e7247a1bfaa67f8c" 325 | integrity sha1-32BBeABfUi8V60SQ5yR6G/qmf4w= 326 | 327 | proxy-addr@~2.0.5: 328 | version "2.0.7" 329 | resolved "https://registry.yarnpkg.com/proxy-addr/-/proxy-addr-2.0.7.tgz#f19fe69ceab311eeb94b42e70e8c2070f9ba1025" 330 | integrity sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg== 331 | dependencies: 332 | forwarded "0.2.0" 333 | ipaddr.js "1.9.1" 334 | 335 | qs@6.7.0: 336 | version "6.7.0" 337 | resolved "https://registry.yarnpkg.com/qs/-/qs-6.7.0.tgz#41dc1a015e3d581f1621776be31afb2876a9b1bc" 338 | integrity sha512-VCdBRNFTX1fyE7Nb6FYoURo/SPe62QCaAyzJvUjwRaIsc+NePBEniHlvxFmmX56+HZphIGtV0XeCirBtpDrTyQ== 339 | 340 | range-parser@~1.2.1: 341 | version "1.2.1" 342 | resolved "https://registry.yarnpkg.com/range-parser/-/range-parser-1.2.1.tgz#3cf37023d199e1c24d1a55b84800c2f3e6468031" 343 | integrity sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg== 344 | 345 | raw-body@2.4.0: 346 | version "2.4.0" 347 | resolved "https://registry.yarnpkg.com/raw-body/-/raw-body-2.4.0.tgz#a1ce6fb9c9bc356ca52e89256ab59059e13d0332" 348 | integrity sha512-4Oz8DUIwdvoa5qMJelxipzi/iJIi40O5cGV1wNYp5hvZP8ZN0T+jiNkL0QepXs+EsQ9XJ8ipEDoiH70ySUJP3Q== 349 | dependencies: 350 | bytes "3.1.0" 351 | http-errors "1.7.2" 352 | iconv-lite "0.4.24" 353 | unpipe "1.0.0" 354 | 355 | safe-buffer@5.1.2: 356 | version "5.1.2" 357 | resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d" 358 | integrity sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g== 359 | 360 | "safer-buffer@>= 2.1.2 < 3": 361 | version "2.1.2" 362 | resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a" 363 | integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg== 364 | 365 | send@0.17.1: 366 | version "0.17.1" 367 | resolved "https://registry.yarnpkg.com/send/-/send-0.17.1.tgz#c1d8b059f7900f7466dd4938bdc44e11ddb376c8" 368 | integrity sha512-BsVKsiGcQMFwT8UxypobUKyv7irCNRHk1T0G680vk88yf6LBByGcZJOTJCrTP2xVN6yI+XjPJcNuE3V4fT9sAg== 369 | dependencies: 370 | debug "2.6.9" 371 | depd "~1.1.2" 372 | destroy "~1.0.4" 373 | encodeurl "~1.0.2" 374 | escape-html "~1.0.3" 375 | etag "~1.8.1" 376 | fresh "0.5.2" 377 | http-errors "~1.7.2" 378 | mime "1.6.0" 379 | ms "2.1.1" 380 | on-finished "~2.3.0" 381 | range-parser "~1.2.1" 382 | statuses "~1.5.0" 383 | 384 | serve-static@1.14.1: 385 | version "1.14.1" 386 | resolved "https://registry.yarnpkg.com/serve-static/-/serve-static-1.14.1.tgz#666e636dc4f010f7ef29970a88a674320898b2f9" 387 | integrity sha512-JMrvUwE54emCYWlTI+hGrGv5I8dEwmco/00EvkzIIsR7MqrHonbD9pO2MOfFnpFntl7ecpZs+3mW+XbQZu9QCg== 388 | dependencies: 389 | encodeurl "~1.0.2" 390 | escape-html "~1.0.3" 391 | parseurl "~1.3.3" 392 | send "0.17.1" 393 | 394 | setprototypeof@1.1.1: 395 | version "1.1.1" 396 | resolved "https://registry.yarnpkg.com/setprototypeof/-/setprototypeof-1.1.1.tgz#7e95acb24aa92f5885e0abef5ba131330d4ae683" 397 | integrity sha512-JvdAWfbXeIGaZ9cILp38HntZSFSo3mWg6xGcJJsd+d4aRMOqauag1C63dJfDw7OaMYwEbHMOxEZ1lqVRYP2OAw== 398 | 399 | "statuses@>= 1.5.0 < 2", statuses@~1.5.0: 400 | version "1.5.0" 401 | resolved "https://registry.yarnpkg.com/statuses/-/statuses-1.5.0.tgz#161c7dac177659fd9811f43771fa99381478628c" 402 | integrity sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow= 403 | 404 | toidentifier@1.0.0: 405 | version "1.0.0" 406 | resolved "https://registry.yarnpkg.com/toidentifier/-/toidentifier-1.0.0.tgz#7e1be3470f1e77948bc43d94a3c8f4d7752ba553" 407 | integrity sha512-yaOH/Pk/VEhBWWTlhI+qXxDFXlejDGcQipMlyxda9nthulaxLZUNcUqFxokp0vcYnvteJln5FNQDRrxj3YcbVw== 408 | 409 | type-is@~1.6.17, type-is@~1.6.18: 410 | version "1.6.18" 411 | resolved "https://registry.yarnpkg.com/type-is/-/type-is-1.6.18.tgz#4e552cd05df09467dcbc4ef739de89f2cf37c131" 412 | integrity sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g== 413 | dependencies: 414 | media-typer "0.3.0" 415 | mime-types "~2.1.24" 416 | 417 | typescript@^4.4.4: 418 | version "4.4.4" 419 | resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.4.4.tgz#2cd01a1a1f160704d3101fd5a58ff0f9fcb8030c" 420 | integrity sha512-DqGhF5IKoBl8WNf8C1gu8q0xZSInh9j1kJJMqT3a94w1JzVaBU4EXOSMrz9yDqMT0xt3selp83fuFMQ0uzv6qA== 421 | 422 | undici@^4.9.5: 423 | version "4.9.5" 424 | resolved "https://registry.yarnpkg.com/undici/-/undici-4.9.5.tgz#6531b6b2587c2c42d77c0dded83d058a328775f8" 425 | integrity sha512-t59IFVYiMnFThboJL9izqwsDEfSbZDPZ/8iCYBCkEFLy63x9m4YaNt0E+r5+X993syC9M0W/ksusZC9YuAamMg== 426 | 427 | unpipe@1.0.0, unpipe@~1.0.0: 428 | version "1.0.0" 429 | resolved "https://registry.yarnpkg.com/unpipe/-/unpipe-1.0.0.tgz#b2bf4ee8514aae6165b4817829d21b2ef49904ec" 430 | integrity sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw= 431 | 432 | utils-merge@1.0.1: 433 | version "1.0.1" 434 | resolved "https://registry.yarnpkg.com/utils-merge/-/utils-merge-1.0.1.tgz#9f95710f50a267947b2ccc124741c1028427e713" 435 | integrity sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM= 436 | 437 | vary@~1.1.2: 438 | version "1.1.2" 439 | resolved "https://registry.yarnpkg.com/vary/-/vary-1.1.2.tgz#2299f02c6ded30d4a5961b0b9f74524a18f634fc" 440 | integrity sha1-IpnwLG3tMNSllhsLn3RSShj2NPw= 441 | --------------------------------------------------------------------------------