├── .github ├── dependabot.yml └── workflows │ ├── check-pr.yml │ └── publish-npm-packages.yml ├── .gitignore ├── .npmignore ├── .prettierrc ├── .vscode └── settings.json ├── LICENSE ├── README.md ├── docs └── assets │ ├── demo.gif │ ├── logo-dark.svg │ └── logo-light.svg ├── jest.config.js ├── package.json ├── pnpm-lock.yaml ├── src ├── index.ts └── utils │ └── error.ts ├── tests ├── errors │ └── 0_errors.test.ts ├── middlewares │ ├── 0_simple-middleware.test.ts │ └── 1_chained-middleware.test.ts ├── misc │ └── 0_no-transformer.test.ts └── validation │ ├── 0_body-validation.test.ts │ ├── 1_header-validation.test.ts │ ├── 2_params-validation.test.ts │ ├── 3_query-validation.test.ts │ ├── 4_combined-validation.test.ts │ └── 5_validation_transforms.test.ts └── tsconfig.json /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | # Enable version updates package.json 4 | - package-ecosystem: 'npm' 5 | directory: '/' 6 | schedule: 7 | interval: 'weekly' 8 | -------------------------------------------------------------------------------- /.github/workflows/check-pr.yml: -------------------------------------------------------------------------------- 1 | name: '[PR] Tests pass and build is successful' 2 | 3 | on: 4 | workflow_dispatch: 5 | pull_request: 6 | 7 | jobs: 8 | check-pr: 9 | runs-on: ubuntu-latest 10 | steps: 11 | - uses: actions/checkout@v2 12 | - uses: actions/setup-node@v4 13 | with: 14 | node-version: '20.x' 15 | - run: npm install -g pnpm 16 | - run: pnpm install --frozen-lockfile 17 | - run: pnpm test 18 | - run: pnpm build 19 | -------------------------------------------------------------------------------- /.github/workflows/publish-npm-packages.yml: -------------------------------------------------------------------------------- 1 | name: Publish Package to npmjs 2 | on: 3 | workflow_dispatch: 4 | release: 5 | types: [published] 6 | jobs: 7 | build: 8 | runs-on: ubuntu-latest 9 | steps: 10 | - uses: actions/checkout@v4 11 | # Setup .npmrc file to publish to npm 12 | - uses: actions/setup-node@v4 13 | with: 14 | node-version: '20.x' 15 | registry-url: 'https://registry.npmjs.org' 16 | - run: npm install -g pnpm 17 | - run: pnpm install 18 | - run: pnpm test 19 | - run: pnpm build 20 | - run: pnpm publish --access public --no-git-checks 21 | env: 22 | NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} 23 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | node_modules 5 | .pnp 6 | .pnp.js 7 | npm-debug.log* 8 | yarn-debug.log* 9 | yarn-error.log* 10 | 11 | # testing 12 | coverage/ 13 | 14 | # IntelliJ 15 | .idea/ 16 | 17 | # misc 18 | .DS_Store 19 | *.pem 20 | 21 | # ts 22 | *.tsbuildinfo 23 | 24 | # local env files 25 | .env 26 | .env.local 27 | .env.development.local 28 | .env.test.local 29 | .env.production.local 30 | 31 | # project 32 | dist/ -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | # needs to be explicity allowed as it is ignored by the 2 | # .gitignore, which is also taken into account by `npm package` 3 | !dist/ 4 | 5 | # testing 6 | coverage/ 7 | 8 | .vscode/ 9 | .github/ 10 | docs/ 11 | src/ 12 | examples/ -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "trailingComma": "es5", 3 | "tabWidth": 4, 4 | "semi": false, 5 | "singleQuote": true, 6 | "printWidth": 100 7 | } 8 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "eslint.workingDirectories": [ 3 | { 4 | "mode": "auto" 5 | } 6 | ] 7 | } 8 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2024 Lightning Technologies GmbH 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 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |

2 |

3 | 4 | 5 | Catena 6 | 7 |

8 | 9 |

A library for building type-safe Node.js APIs

10 | 11 |

12 | Installation • 13 | Examples • 14 | Documentation 15 |

16 | 17 |
18 | 19 | # Build type-safe APIs 20 | 21 | Catena is an lightweight library for building end-to-end type-safe APIs on top of Express. It's inspired by tRPC but unlike tRPC, you can just plug it into your existing Express codebase. 22 | The main goal is to have a unified chain of useful steps to safely handle requests with a great DX and minimal overhead. 23 | 24 |
25 | Demo 26 |
27 |

28 | A demo of type sharing between route handler (backend) and frontend, inspired by tRPC. 29 |

30 |
31 |
32 | 33 | # Installation 34 | 35 | 1. Install Catena and its peer dependencies 36 | 37 | ``` 38 | npm i @sonic-tech/catena zod express 39 | ``` 40 | 41 | 2. Also make sure that you have Express' types installed, if you want to leverage the full type-safeness. 42 | 43 | ``` 44 | npm i -D @types/express 45 | ``` 46 | 47 | # Documentation 48 | 49 | - [Examples](#examples) 50 | - [Setting up Catena with Express](#setting-up-catena--express) 51 | - [Core Concepts](#core-concepts) 52 | - [Chaining](#chaining) 53 | - [Validators](#validators) 54 | - [Middlewares](#middlewares-1) 55 | - [Resolver](#resolver) 56 | - [Transformer](#transformer) 57 | - [Type-sharing across codebases](#type-sharing-across-codebases) 58 | - [File-based Routing](#file-based-routing) 59 | 60 | # Examples 61 | 62 | ## A simple handler 63 | 64 | The first example is as simple as it gets. Looks like a normal express handler and also does the same. 65 | 66 | ```ts 67 | // ... 68 | import { Handler } from '@sonic-tech/catena' 69 | 70 | const app = express() 71 | app.use(express.json()) 72 | 73 | app.get( 74 | '/', 75 | new Handler() 76 | .resolve((req, res) => { 77 | res.status(200).send('Hello World') 78 | }) 79 | // Make sure that `.express()` always is the last method in the handler chain. It converts the logical chain into an express handler. 80 | .express() 81 | ) 82 | ``` 83 | 84 | ## Validating requests 85 | 86 | There are many occasions where you want to validate parts of the incoming data. Here's how Catena does it. 87 | 88 | ```ts 89 | // ... 90 | import { Handler } from '@sonic-tech/catena' 91 | 92 | const app = express() 93 | app.use(express.json()); 94 | 95 | 96 | app.post( 97 | '/user/:uuid', 98 | new Handler(). 99 | .validate("params", { 100 | uuid: z.string().uuid() 101 | }) 102 | .validate("body", { 103 | username: z.string().optional(), 104 | age: z.number().min(13).optional(), 105 | email: z.string().email(), 106 | }) 107 | .resolve(async (req, res) => { 108 | // All 4 properties are strongly typed based on the zod schemas 109 | const { uuid } = req.params 110 | const { username, age, email } = req.body 111 | 112 | // ... 113 | 114 | res.status(200).send("User created!") 115 | }) 116 | .express() 117 | ) 118 | ``` 119 | 120 | ## Transformers 121 | 122 | If you want to have a secure and unified way of returning data to the client, use transformers. 123 | Transformers let you create and send JSON DTOs based on the data that the resolver returned. 124 | 125 | This is the recommended way of returning data to the client. 126 | 127 | ```ts 128 | // ... 129 | import { Handler } from '@sonic-tech/catena' 130 | 131 | const app = express() 132 | app.use(express.json()); 133 | 134 | 135 | app.get( 136 | '/user/:uuid', 137 | new Handler(). 138 | .validate("params", { 139 | uuid: z.string().uuid() 140 | }) 141 | .resolve(async (req, res) => { 142 | const userIncludingPassword = await UserService.getUser(req.uuid) 143 | 144 | return userIncludingPassword 145 | }) 146 | .transform((data) => { 147 | return { 148 | data: { 149 | uuid: data.uuid, 150 | email: data.email 151 | } 152 | } 153 | }) 154 | .express() 155 | ) 156 | ``` 157 | 158 | ## Middlewares 159 | 160 | Catena extends middlewares by establishing a shared context that can be passed from one middleware to another and finally to the resolver. You can write inline middlewares or just pass in a function. 161 | 162 | ```ts 163 | // ... 164 | import { Handler, HTTPError } from '@sonic-tech/catena' 165 | import { AnotherMiddleware } from "..." 166 | 167 | const app = express() 168 | app.use(express.json()); 169 | 170 | 171 | app.get( 172 | '/user/:uuid', 173 | new Handler(). 174 | .validate("params", { 175 | uuid: z.string().uuid() 176 | }) 177 | .validate("headers", { 178 | // key in header validations must always be lower-case! 179 | authorization: z.string() 180 | }) 181 | .middleware((req) => {j 182 | const requestingUser = await SecurityService.getAuthorizedUser(req.headers.authorization); 183 | if (!requestingUser) { 184 | // Throw errors when you want to stop further request processing while returning an error at the same time 185 | throw new HTTPError(400, 'This should fail') 186 | } 187 | 188 | return { 189 | requestingUser 190 | } 191 | }) 192 | .middleware(AnotherMiddleware) 193 | .resolve(async (req, res, context) => { 194 | // You can access the merged type-safe context of all middlewares in the resolver 195 | const { requestingUser } = context 196 | 197 | 198 | const userIncludingPassword = await UserService.getUser(req.uuid) 199 | 200 | return userIncludingPassword 201 | }) 202 | .transform((data) => { 203 | return { 204 | data: { 205 | uuid: data.uuid, 206 | email: data.email 207 | } 208 | } 209 | }) 210 | .express() 211 | ) 212 | ``` 213 | 214 | ## Setting up Catena + Express 215 | 216 | Catena currently supports Express; work is underway to support Next.js. 217 | 218 | To setup Catena with Express make sure to do the following: 219 | 220 | - Install `express` and `@types/express` using yarn/npm/pnpm/bun 221 | - Suffix all Catena handlers with `.express()` (!). This function will transform the Catena chain into an Express request handler. Without appending this method, you're going to get a type-error and the handler will not work. 222 | 223 | Also, you have to use the `express.json()` (or body-parser) middleware in order for the validators to work with JSON content. 224 | 225 | ```ts 226 | const app = express() 227 | app.use(express.json()) 228 | ``` 229 | 230 | ## Core Concepts 231 | 232 | ## Chaining 233 | 234 | Catena features a handler that is assembled as a chain out of the following elements. Only the `resolve` element is required, all other elements are optional. 235 | 236 | - **`validate`**: Chain [zod](https://github.com/colinhacks/zod)-powered validators for `body`, `params`, `query` and `headers` that only allow requests that match the zod schema 237 | - **`middleware`**: Middlewares can return values into a shared "context", which is type-safe and accessible to all following chain elements. To block further request processing, e.g. because the requesting entity is not authorized, throw `HTTPError` 238 | - **`resolve`**: The resolver should be used to handle all business logic, like a normal express handler would do. It either returns data that is passed to the transformer or may use the `res` object to send a response without a transformer 239 | - **`transform`**: The transformer can be used to generate a DTO that can be send as response body by just returning it 240 | 241 | The elements in this chain are processed one after the other. So if a middleware is executed after a validator, you can be sure that the validation has been passed. If there are multiple chained middlewares, you always access the context of all previous middlewares in the following middlewares. 242 | 243 | All Catena chain elements are bundled into one actual Express handler with the `.express()` method at the end of the chain. Internally, we are not calling Express' `next()` until all chain elements have completed or there has been an uncaught error. If there is an uncaught error, that is not `HTTPError`, we are calling `next(err)`. You may then handle errors on router level or append another middleware. 244 | 245 | ## Validators 246 | 247 | Validators are virtual middlewares that validate the given request data using zod. You can create validators for `body`, `params`, `query` and `headers`. Objects validated by validators are type guarded for all following middlewares and the resolver. 248 | 249 | ### Method Signature 250 | 251 | `.validate(type: "body" | "params" | "query" | "headers", zodObject: object | z.object)` 252 | 253 | The second parameter can either be a `z.object()` or just a normal object that contains keys with zod validations as values. So both of the following usages are valid: 254 | 255 | ```ts 256 | new Handler().validate('body', { 257 | email: z.string().email(), 258 | }) 259 | // .resolve ... 260 | ``` 261 | 262 | ```ts 263 | new Handler().validate( 264 | 'body', 265 | z.object({ 266 | email: z.string().email(), 267 | }) 268 | // .resolve ... 269 | ) 270 | ``` 271 | 272 | Using just an object is more readable, while using `z.object` has the advantage of being able to infer the validator type and use it e.g. in services as argument type. Example: 273 | 274 | ```ts 275 | const bodyValidation = z.object({ 276 | email: z.string().email(), 277 | }) 278 | 279 | new Handler().validate('body', bodyValidation) //.resolve ... 280 | 281 | const myServiceMethod = (body: z.infer) => { 282 | // ... 283 | } 284 | ``` 285 | 286 | ### Caveats 287 | 288 | - For `headers` validations, **always** use lower-case only keys! ~~`Authorization: z.string()`~~ --> `authorization: z.string()`. The reason for this is that Express converts all headers to lower case to comply with the RFC for HTTP requests, which states that header keys are case-insensitive. 289 | 290 | ## Middlewares 291 | 292 | Middlewares are functions that you can connect prior to the resolver in order to add custom logic such as authorization, non-trivial validation, etc. before the resolver is executed. 293 | 294 | ### Middleware method signature 295 | 296 | ```ts 297 | .middleware(req: RequestWithValidations, res: Response, next: NextFunction, context: MiddlewareContext) 298 | ``` 299 | 300 | - `req` extends the default express object with the type-safe inference made by the validators on `body`, `params`, `query` and `headers`. 301 | - `res` is the default Express `Response` object 302 | - `next` is the default Express `NextFunction` 303 | - `context` is a simple object that is empty at the beginning of the first middleware in the chain. It can be filled with any values by the middlewares and passed to other middlewares and the resolver, see below 304 | 305 | ### Catena Middlewares 306 | 307 | Catena exposes a more straightforward way to write middlewares, while also supporting all existing middlewares. 308 | 309 | #### Context 310 | 311 | Instead of overwriting or extending some parts of the `req` object to pass along context, you can just return an object. This object will be merged with the existing `context` object and will be accessible to all following middlewares and the resolver (type-safe, of course). 312 | 313 | ```ts 314 | import { Handler, HTTPError } from '@sonic-tech/catena' 315 | 316 | new Handler() 317 | .validate('params', { 318 | uuid: z.string().uuid(), 319 | }) 320 | .middleware(async (req) => { 321 | const user = await UserService.getUser(req.params.uuid) // -> { email: "...", uuid: "..." } 322 | 323 | return { 324 | user, 325 | } 326 | }) 327 | .middleware(async (req, res, next, context) => { 328 | // email and uuid can be extracted from the context type-safely 329 | const { email, uuid } = context.user 330 | 331 | if (!email.endsWith('@mycompany.com')) { 332 | throw new HTTPError(403, 'Company not allowed') 333 | } 334 | 335 | const organization = await OrganizationService().getByUser(uuid) 336 | 337 | return { 338 | organization, 339 | } 340 | }) 341 | .resolve(async (req, res, context) => { 342 | /** 343 | * The resolver has access to both user and organization 344 | * since the context objects have been merged 345 | */ 346 | const { user, organization } = context 347 | 348 | // ... 349 | }) 350 | .express() 351 | ``` 352 | 353 | A middleware does not need to return anything. If it returns void, the context objects stays as is. 354 | 355 | Sending something to the client using `res.send` causes the chain to stop, i.e. the next chain element is not executed. 356 | 357 | #### Errors 358 | 359 | Instead of using `res.send` to send error messages, you can import `HTTPError` from Catena and use `throw new HTTPError(statusCode, errorMessage)` which will automatically resolve the request using the given status code and a standardized error message. An example can be seen in the code snippet of the last section. 360 | 361 | ### Existing Middlewares 362 | 363 | You can also use any middleware that works with express out of the box using the default `(req, res, next)` syntax. 364 | 365 | ```ts 366 | import { MySecondMiddleware } from "../middlewares" 367 | const MyExistingMiddleware = (req, res, next) => { 368 | if(!req.body.continue) { 369 | res.status(400).send("Bad Request") 370 | return 371 | } 372 | 373 | next() 374 | } 375 | 376 | new Handler() 377 | .middleware(MyExistingMiddleware) 378 | .middleware(MySecondMiddleware) 379 | .resolve(...) 380 | .transform(...) 381 | .express() 382 | ``` 383 | 384 | ## Resolver 385 | 386 | The resolver may be used as the core handler of the request, just as you used the handler function before. 387 | 388 | ### Method Signature 389 | 390 | ```ts 391 | .resolve((req: RequestWithValidations, res: Response, context: MiddlewareContext) => any | Promise) 392 | ``` 393 | 394 | - The `req` is the default Express request object, except that `req.body`, `req.params`, `req.query` and `req.headers` are typed by the infered zod validations from the validators 395 | - The `res` object is the default Express response object 396 | - The `context` object is the merged Context you passed along the middlewares 397 | 398 | ### Context 399 | 400 | As with middlewares, you can access the merged context of all previous middlewares (= all middlewares) through the `context` object. 401 | 402 | ### Return Values 403 | 404 | You can use `res` to send responses directly to the client without a transformer. 405 | 406 | However, it is recommended to return values instead. Those values are then passed to the [Transformer](#transformer) instead of being sent to the client directly. 407 | 408 | ```ts 409 | new Handler() 410 | .middleware(() => { 411 | return { 412 | user: { 413 | uuid: '...', 414 | }, 415 | } 416 | }) 417 | .resolve((req, res, context) => { 418 | const { user } = context 419 | 420 | const subscription = await UserService.getUserSubscription(user.uuid) 421 | 422 | // Just pass the data to the transformer instead of using res.send. 423 | return subscription 424 | }) 425 | .transform((data) => { 426 | // data has the type of "subscription" 427 | return { 428 | userSubscriptionUuid: data.uuid, 429 | } 430 | }) 431 | .express() 432 | ``` 433 | 434 | ## Transformer 435 | 436 | Transformers can be used to create sanitized data transfer objects based on the resolved data. This enables your resolver to just handle the business logic, independent of what single values should or should not be returned to the client. 437 | 438 | For example, the resolver might get a user object from the database for a given query. Of course you don't want to return the user's password to the client. To keep things clean, just pass the user object from the resolver (which shouldn't have to care about sanitizing values) to the transformer, which then takes the given object and only returns values that are appropriate to be returned. 439 | 440 | Example 441 | 442 | ```ts 443 | new Handler() 444 | .validate('params', { 445 | uuid: z.string().uuid(), 446 | }) 447 | .resolve(async (req) => { 448 | const { uuid } = req.params 449 | 450 | // This object contains confidential information, like a password 451 | const user = await UserService.getUser(uuid) 452 | 453 | return user 454 | }) 455 | .transform((data) => { 456 | return { 457 | uuid: data.uuid, 458 | email: data.email, 459 | // leave out the password, since we don't want to send it to the client 460 | } 461 | }) 462 | .express() 463 | ``` 464 | 465 | You may return any value. If you choose to return an object or array, they are send with `res.json()`. All other return values are send with `res.send()`. 466 | 467 | If you want to use a different status code, set headers, cookie, etc. you can use e.g. `res.status(201)` before the return statement. We'll use this `res` object for sending the response internally 468 | 469 | ## Type-sharing across codebases 470 | 471 | Request and response types of API handlers can be inferred and exported. You can use this to share your types e.g. with the frontend, like tRPC does. 472 | 473 | Each handler exposes a `types` interface that contains `request` and `response` types. 474 | 475 | The request type contains the expected types for `body`, `query`, `params` and `headers`. 476 | 477 | The `response` type represents the type of the value that is returned by the transformer. 478 | 479 | ### Example 480 | 481 | **Backend** 482 | 483 | ```ts backend/handler.ts 484 | const myRequestHandler = new Handler() 485 | .validate("body", { 486 | email: z.string().email() 487 | }) 488 | .resolve(...) 489 | .transform((data) => { 490 | return { 491 | data: { 492 | uuid: data.uuid, 493 | email: data.email, 494 | age: data.age 495 | } 496 | } 497 | }) 498 | .express() 499 | 500 | app.get("/user", myRequestHandler); 501 | 502 | 503 | export type GetUserTypes = typeof myRequestHandler.types; 504 | 505 | ``` 506 | 507 | **Frontend** 508 | 509 | ```ts frontend/requests.ts 510 | // It's important to only import using `import type`. This way, no business logic will be leaked to the frontend but just the type 511 | import type { GetUserTypes } from 'backend/handler' 512 | 513 | /** 514 | * Body type inferred as 515 | * { 516 | * email: string 517 | * } 518 | */ 519 | 520 | const body: GetUserTypes['request']['body'] = { 521 | email: 'test@test.com', 522 | } 523 | 524 | const myRequestResponse = await fetch('/user', { 525 | body: JSON.stringify(body), 526 | }) 527 | 528 | /** 529 | * Type inferred as 530 | * { 531 | * data: { 532 | * uuid: string; 533 | * email: string; 534 | * age: number; 535 | * } 536 | */ 537 | const responseData: GetUserTypes['response'] = await myRequestResponse.json() 538 | ``` 539 | 540 | ## File-based Routing 541 | 542 | We found [express-file-routing](https://github.com/matthiaaas/express-file-routing) to be a great addition to projects that use Catena, if you like file-based routing. 543 | 544 | An example of Catena + file-based routing: 545 | 546 | ```ts 547 | // /users/[uuid].ts 548 | 549 | export const POST = new Handler() 550 | .validate('params', { 551 | uuid: z.string().uuid(), 552 | }) 553 | .validate('body', { 554 | email: z.string().email().optional(), 555 | firstName: z.string().optional(), 556 | lastName: z.string().optional(), 557 | }) 558 | .resolve((req) => { 559 | const updatedUser = new UserService.updateUser(req.params.uuid) 560 | 561 | return updatedUser 562 | }) 563 | .transform((user) => { 564 | return { 565 | uuid: user.uuid, 566 | email: user.email, 567 | name: user.firstName + ' ' + user.lastName, 568 | } 569 | }) 570 | .express() 571 | ``` 572 | -------------------------------------------------------------------------------- /docs/assets/demo.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sonic-technology/catena/faa824f75cd04e559e042720bfe7b47e3694466a/docs/assets/demo.gif -------------------------------------------------------------------------------- /docs/assets/logo-dark.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /docs/assets/logo-light.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /jest.config.js: -------------------------------------------------------------------------------- 1 | /** @type {import('ts-jest').JestConfigWithTsJest} */ 2 | export default { 3 | testEnvironment: 'node', 4 | preset: 'ts-jest/presets/default-esm', 5 | testMatch: ['/**/tests/**/*.test.ts'], 6 | moduleNameMapper: { 7 | // @/index.js -> src/index.ts 8 | // '^@/(.*)$': '/src/$1', 9 | '^(..?/.+).js?$': '$1', 10 | }, 11 | extensionsToTreatAsEsm: ['.ts'], 12 | transform: { 13 | '^.+\\.[jt]s?$': [ 14 | 'ts-jest', 15 | { 16 | useESM: true, 17 | tsconfig: { 18 | allowJs: true, 19 | }, 20 | }, 21 | ], 22 | }, 23 | } 24 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@sonic-tech/catena", 3 | "version": "0.2.0", 4 | "type": "module", 5 | "description": "A lightweight and extensible library for building robust Node.js APIs, fast.", 6 | "main": "./dist/index.js", 7 | "types": "./dist/index.d.ts", 8 | "scripts": { 9 | "build": "rm -rf ./dist tsconfig.tsbuildinfo && tsc", 10 | "test": "NODE_OPTIONS=--experimental-vm-modules jest" 11 | }, 12 | "peerDependencies": { 13 | "express": "^4.18.2", 14 | "zod": "^3.22.4" 15 | }, 16 | "keywords": [], 17 | "repository": { 18 | "type": "git", 19 | "url": "https://github.com/sonic-technology/catena" 20 | }, 21 | "author": "Sonic Technologies", 22 | "license": "MIT", 23 | "devDependencies": { 24 | "@types/express": "^4.17.20", 25 | "@types/jest": "^29.5.6", 26 | "@types/supertest": "^6.0.2", 27 | "jest": "^29.7.0", 28 | "supertest": "^6.3.3", 29 | "ts-jest": "^29.1.1", 30 | "typescript": "^5.2.2" 31 | }, 32 | "packageManager": "pnpm@8.6.10" 33 | } 34 | -------------------------------------------------------------------------------- /pnpm-lock.yaml: -------------------------------------------------------------------------------- 1 | lockfileVersion: '6.0' 2 | 3 | settings: 4 | autoInstallPeers: true 5 | excludeLinksFromLockfile: false 6 | 7 | dependencies: 8 | express: 9 | specifier: ^4.18.2 10 | version: 4.18.2 11 | zod: 12 | specifier: ^3.22.4 13 | version: 3.22.4 14 | 15 | devDependencies: 16 | '@types/express': 17 | specifier: ^4.17.20 18 | version: 4.17.21 19 | '@types/jest': 20 | specifier: ^29.5.6 21 | version: 29.5.12 22 | '@types/supertest': 23 | specifier: ^6.0.2 24 | version: 6.0.2 25 | jest: 26 | specifier: ^29.7.0 27 | version: 29.7.0 28 | supertest: 29 | specifier: ^6.3.3 30 | version: 6.3.4 31 | ts-jest: 32 | specifier: ^29.1.1 33 | version: 29.1.5(@babel/core@7.23.9)(jest@29.7.0)(typescript@5.5.3) 34 | typescript: 35 | specifier: ^5.2.2 36 | version: 5.5.3 37 | 38 | packages: 39 | 40 | /@ampproject/remapping@2.2.1: 41 | resolution: {integrity: sha512-lFMjJTrFL3j7L9yBxwYfCq2k6qqwHyzuUl/XBnif78PWTJYyL/dfowQHWE3sp6U6ZzqWiiIZnpTMO96zhkjwtg==} 42 | engines: {node: '>=6.0.0'} 43 | dependencies: 44 | '@jridgewell/gen-mapping': 0.3.3 45 | '@jridgewell/trace-mapping': 0.3.22 46 | dev: true 47 | 48 | /@babel/code-frame@7.23.5: 49 | resolution: {integrity: sha512-CgH3s1a96LipHCmSUmYFPwY7MNx8C3avkq7i4Wl3cfa662ldtUe4VM1TPXX70pfmrlWTb6jLqTYrZyT2ZTJBgA==} 50 | engines: {node: '>=6.9.0'} 51 | dependencies: 52 | '@babel/highlight': 7.23.4 53 | chalk: 2.4.2 54 | dev: true 55 | 56 | /@babel/compat-data@7.23.5: 57 | resolution: {integrity: sha512-uU27kfDRlhfKl+w1U6vp16IuvSLtjAxdArVXPa9BvLkrr7CYIsxH5adpHObeAGY/41+syctUWOZ140a2Rvkgjw==} 58 | engines: {node: '>=6.9.0'} 59 | dev: true 60 | 61 | /@babel/core@7.23.9: 62 | resolution: {integrity: sha512-5q0175NOjddqpvvzU+kDiSOAk4PfdO6FvwCWoQ6RO7rTzEe8vlo+4HVfcnAREhD4npMs0e9uZypjTwzZPCf/cw==} 63 | engines: {node: '>=6.9.0'} 64 | dependencies: 65 | '@ampproject/remapping': 2.2.1 66 | '@babel/code-frame': 7.23.5 67 | '@babel/generator': 7.23.6 68 | '@babel/helper-compilation-targets': 7.23.6 69 | '@babel/helper-module-transforms': 7.23.3(@babel/core@7.23.9) 70 | '@babel/helpers': 7.23.9 71 | '@babel/parser': 7.23.9 72 | '@babel/template': 7.23.9 73 | '@babel/traverse': 7.23.9 74 | '@babel/types': 7.23.9 75 | convert-source-map: 2.0.0 76 | debug: 4.3.4 77 | gensync: 1.0.0-beta.2 78 | json5: 2.2.3 79 | semver: 6.3.1 80 | transitivePeerDependencies: 81 | - supports-color 82 | dev: true 83 | 84 | /@babel/generator@7.23.6: 85 | resolution: {integrity: sha512-qrSfCYxYQB5owCmGLbl8XRpX1ytXlpueOb0N0UmQwA073KZxejgQTzAmJezxvpwQD9uGtK2shHdi55QT+MbjIw==} 86 | engines: {node: '>=6.9.0'} 87 | dependencies: 88 | '@babel/types': 7.23.9 89 | '@jridgewell/gen-mapping': 0.3.3 90 | '@jridgewell/trace-mapping': 0.3.22 91 | jsesc: 2.5.2 92 | dev: true 93 | 94 | /@babel/helper-compilation-targets@7.23.6: 95 | resolution: {integrity: sha512-9JB548GZoQVmzrFgp8o7KxdgkTGm6xs9DW0o/Pim72UDjzr5ObUQ6ZzYPqA+g9OTS2bBQoctLJrky0RDCAWRgQ==} 96 | engines: {node: '>=6.9.0'} 97 | dependencies: 98 | '@babel/compat-data': 7.23.5 99 | '@babel/helper-validator-option': 7.23.5 100 | browserslist: 4.22.3 101 | lru-cache: 5.1.1 102 | semver: 6.3.1 103 | dev: true 104 | 105 | /@babel/helper-environment-visitor@7.22.20: 106 | resolution: {integrity: sha512-zfedSIzFhat/gFhWfHtgWvlec0nqB9YEIVrpuwjruLlXfUSnA8cJB0miHKwqDnQ7d32aKo2xt88/xZptwxbfhA==} 107 | engines: {node: '>=6.9.0'} 108 | dev: true 109 | 110 | /@babel/helper-function-name@7.23.0: 111 | resolution: {integrity: sha512-OErEqsrxjZTJciZ4Oo+eoZqeW9UIiOcuYKRJA4ZAgV9myA+pOXhhmpfNCKjEH/auVfEYVFJ6y1Tc4r0eIApqiw==} 112 | engines: {node: '>=6.9.0'} 113 | dependencies: 114 | '@babel/template': 7.23.9 115 | '@babel/types': 7.23.9 116 | dev: true 117 | 118 | /@babel/helper-hoist-variables@7.22.5: 119 | resolution: {integrity: sha512-wGjk9QZVzvknA6yKIUURb8zY3grXCcOZt+/7Wcy8O2uctxhplmUPkOdlgoNhmdVee2c92JXbf1xpMtVNbfoxRw==} 120 | engines: {node: '>=6.9.0'} 121 | dependencies: 122 | '@babel/types': 7.23.9 123 | dev: true 124 | 125 | /@babel/helper-module-imports@7.22.15: 126 | resolution: {integrity: sha512-0pYVBnDKZO2fnSPCrgM/6WMc7eS20Fbok+0r88fp+YtWVLZrp4CkafFGIp+W0VKw4a22sgebPT99y+FDNMdP4w==} 127 | engines: {node: '>=6.9.0'} 128 | dependencies: 129 | '@babel/types': 7.23.9 130 | dev: true 131 | 132 | /@babel/helper-module-transforms@7.23.3(@babel/core@7.23.9): 133 | resolution: {integrity: sha512-7bBs4ED9OmswdfDzpz4MpWgSrV7FXlc3zIagvLFjS5H+Mk7Snr21vQ6QwrsoCGMfNC4e4LQPdoULEt4ykz0SRQ==} 134 | engines: {node: '>=6.9.0'} 135 | peerDependencies: 136 | '@babel/core': ^7.0.0 137 | dependencies: 138 | '@babel/core': 7.23.9 139 | '@babel/helper-environment-visitor': 7.22.20 140 | '@babel/helper-module-imports': 7.22.15 141 | '@babel/helper-simple-access': 7.22.5 142 | '@babel/helper-split-export-declaration': 7.22.6 143 | '@babel/helper-validator-identifier': 7.22.20 144 | dev: true 145 | 146 | /@babel/helper-plugin-utils@7.22.5: 147 | resolution: {integrity: sha512-uLls06UVKgFG9QD4OeFYLEGteMIAa5kpTPcFL28yuCIIzsf6ZyKZMllKVOCZFhiZ5ptnwX4mtKdWCBE/uT4amg==} 148 | engines: {node: '>=6.9.0'} 149 | dev: true 150 | 151 | /@babel/helper-simple-access@7.22.5: 152 | resolution: {integrity: sha512-n0H99E/K+Bika3++WNL17POvo4rKWZ7lZEp1Q+fStVbUi8nxPQEBOlTmCOxW/0JsS56SKKQ+ojAe2pHKJHN35w==} 153 | engines: {node: '>=6.9.0'} 154 | dependencies: 155 | '@babel/types': 7.23.9 156 | dev: true 157 | 158 | /@babel/helper-split-export-declaration@7.22.6: 159 | resolution: {integrity: sha512-AsUnxuLhRYsisFiaJwvp1QF+I3KjD5FOxut14q/GzovUe6orHLesW2C7d754kRm53h5gqrz6sFl6sxc4BVtE/g==} 160 | engines: {node: '>=6.9.0'} 161 | dependencies: 162 | '@babel/types': 7.23.9 163 | dev: true 164 | 165 | /@babel/helper-string-parser@7.23.4: 166 | resolution: {integrity: sha512-803gmbQdqwdf4olxrX4AJyFBV/RTr3rSmOj0rKwesmzlfhYNDEs+/iOcznzpNWlJlIlTJC2QfPFcHB6DlzdVLQ==} 167 | engines: {node: '>=6.9.0'} 168 | dev: true 169 | 170 | /@babel/helper-validator-identifier@7.22.20: 171 | resolution: {integrity: sha512-Y4OZ+ytlatR8AI+8KZfKuL5urKp7qey08ha31L8b3BwewJAoJamTzyvxPR/5D+KkdJCGPq/+8TukHBlY10FX9A==} 172 | engines: {node: '>=6.9.0'} 173 | dev: true 174 | 175 | /@babel/helper-validator-option@7.23.5: 176 | resolution: {integrity: sha512-85ttAOMLsr53VgXkTbkx8oA6YTfT4q7/HzXSLEYmjcSTJPMPQtvq1BD79Byep5xMUYbGRzEpDsjUf3dyp54IKw==} 177 | engines: {node: '>=6.9.0'} 178 | dev: true 179 | 180 | /@babel/helpers@7.23.9: 181 | resolution: {integrity: sha512-87ICKgU5t5SzOT7sBMfCOZQ2rHjRU+Pcb9BoILMYz600W6DkVRLFBPwQ18gwUVvggqXivaUakpnxWQGbpywbBQ==} 182 | engines: {node: '>=6.9.0'} 183 | dependencies: 184 | '@babel/template': 7.23.9 185 | '@babel/traverse': 7.23.9 186 | '@babel/types': 7.23.9 187 | transitivePeerDependencies: 188 | - supports-color 189 | dev: true 190 | 191 | /@babel/highlight@7.23.4: 192 | resolution: {integrity: sha512-acGdbYSfp2WheJoJm/EBBBLh/ID8KDc64ISZ9DYtBmC8/Q204PZJLHyzeB5qMzJ5trcOkybd78M4x2KWsUq++A==} 193 | engines: {node: '>=6.9.0'} 194 | dependencies: 195 | '@babel/helper-validator-identifier': 7.22.20 196 | chalk: 2.4.2 197 | js-tokens: 4.0.0 198 | dev: true 199 | 200 | /@babel/parser@7.23.9: 201 | resolution: {integrity: sha512-9tcKgqKbs3xGJ+NtKF2ndOBBLVwPjl1SHxPQkd36r3Dlirw3xWUeGaTbqr7uGZcTaxkVNwc+03SVP7aCdWrTlA==} 202 | engines: {node: '>=6.0.0'} 203 | hasBin: true 204 | dependencies: 205 | '@babel/types': 7.23.9 206 | dev: true 207 | 208 | /@babel/plugin-syntax-async-generators@7.8.4(@babel/core@7.23.9): 209 | resolution: {integrity: sha512-tycmZxkGfZaxhMRbXlPXuVFpdWlXpir2W4AMhSJgRKzk/eDlIXOhb2LHWoLpDF7TEHylV5zNhykX6KAgHJmTNw==} 210 | peerDependencies: 211 | '@babel/core': ^7.0.0-0 212 | dependencies: 213 | '@babel/core': 7.23.9 214 | '@babel/helper-plugin-utils': 7.22.5 215 | dev: true 216 | 217 | /@babel/plugin-syntax-bigint@7.8.3(@babel/core@7.23.9): 218 | resolution: {integrity: sha512-wnTnFlG+YxQm3vDxpGE57Pj0srRU4sHE/mDkt1qv2YJJSeUAec2ma4WLUnUPeKjyrfntVwe/N6dCXpU+zL3Npg==} 219 | peerDependencies: 220 | '@babel/core': ^7.0.0-0 221 | dependencies: 222 | '@babel/core': 7.23.9 223 | '@babel/helper-plugin-utils': 7.22.5 224 | dev: true 225 | 226 | /@babel/plugin-syntax-class-properties@7.12.13(@babel/core@7.23.9): 227 | resolution: {integrity: sha512-fm4idjKla0YahUNgFNLCB0qySdsoPiZP3iQE3rky0mBUtMZ23yDJ9SJdg6dXTSDnulOVqiF3Hgr9nbXvXTQZYA==} 228 | peerDependencies: 229 | '@babel/core': ^7.0.0-0 230 | dependencies: 231 | '@babel/core': 7.23.9 232 | '@babel/helper-plugin-utils': 7.22.5 233 | dev: true 234 | 235 | /@babel/plugin-syntax-import-meta@7.10.4(@babel/core@7.23.9): 236 | resolution: {integrity: sha512-Yqfm+XDx0+Prh3VSeEQCPU81yC+JWZ2pDPFSS4ZdpfZhp4MkFMaDC1UqseovEKwSUpnIL7+vK+Clp7bfh0iD7g==} 237 | peerDependencies: 238 | '@babel/core': ^7.0.0-0 239 | dependencies: 240 | '@babel/core': 7.23.9 241 | '@babel/helper-plugin-utils': 7.22.5 242 | dev: true 243 | 244 | /@babel/plugin-syntax-json-strings@7.8.3(@babel/core@7.23.9): 245 | resolution: {integrity: sha512-lY6kdGpWHvjoe2vk4WrAapEuBR69EMxZl+RoGRhrFGNYVK8mOPAW8VfbT/ZgrFbXlDNiiaxQnAtgVCZ6jv30EA==} 246 | peerDependencies: 247 | '@babel/core': ^7.0.0-0 248 | dependencies: 249 | '@babel/core': 7.23.9 250 | '@babel/helper-plugin-utils': 7.22.5 251 | dev: true 252 | 253 | /@babel/plugin-syntax-jsx@7.23.3(@babel/core@7.23.9): 254 | resolution: {integrity: sha512-EB2MELswq55OHUoRZLGg/zC7QWUKfNLpE57m/S2yr1uEneIgsTgrSzXP3NXEsMkVn76OlaVVnzN+ugObuYGwhg==} 255 | engines: {node: '>=6.9.0'} 256 | peerDependencies: 257 | '@babel/core': ^7.0.0-0 258 | dependencies: 259 | '@babel/core': 7.23.9 260 | '@babel/helper-plugin-utils': 7.22.5 261 | dev: true 262 | 263 | /@babel/plugin-syntax-logical-assignment-operators@7.10.4(@babel/core@7.23.9): 264 | resolution: {integrity: sha512-d8waShlpFDinQ5MtvGU9xDAOzKH47+FFoney2baFIoMr952hKOLp1HR7VszoZvOsV/4+RRszNY7D17ba0te0ig==} 265 | peerDependencies: 266 | '@babel/core': ^7.0.0-0 267 | dependencies: 268 | '@babel/core': 7.23.9 269 | '@babel/helper-plugin-utils': 7.22.5 270 | dev: true 271 | 272 | /@babel/plugin-syntax-nullish-coalescing-operator@7.8.3(@babel/core@7.23.9): 273 | resolution: {integrity: sha512-aSff4zPII1u2QD7y+F8oDsz19ew4IGEJg9SVW+bqwpwtfFleiQDMdzA/R+UlWDzfnHFCxxleFT0PMIrR36XLNQ==} 274 | peerDependencies: 275 | '@babel/core': ^7.0.0-0 276 | dependencies: 277 | '@babel/core': 7.23.9 278 | '@babel/helper-plugin-utils': 7.22.5 279 | dev: true 280 | 281 | /@babel/plugin-syntax-numeric-separator@7.10.4(@babel/core@7.23.9): 282 | resolution: {integrity: sha512-9H6YdfkcK/uOnY/K7/aA2xpzaAgkQn37yzWUMRK7OaPOqOpGS1+n0H5hxT9AUw9EsSjPW8SVyMJwYRtWs3X3ug==} 283 | peerDependencies: 284 | '@babel/core': ^7.0.0-0 285 | dependencies: 286 | '@babel/core': 7.23.9 287 | '@babel/helper-plugin-utils': 7.22.5 288 | dev: true 289 | 290 | /@babel/plugin-syntax-object-rest-spread@7.8.3(@babel/core@7.23.9): 291 | resolution: {integrity: sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA==} 292 | peerDependencies: 293 | '@babel/core': ^7.0.0-0 294 | dependencies: 295 | '@babel/core': 7.23.9 296 | '@babel/helper-plugin-utils': 7.22.5 297 | dev: true 298 | 299 | /@babel/plugin-syntax-optional-catch-binding@7.8.3(@babel/core@7.23.9): 300 | resolution: {integrity: sha512-6VPD0Pc1lpTqw0aKoeRTMiB+kWhAoT24PA+ksWSBrFtl5SIRVpZlwN3NNPQjehA2E/91FV3RjLWoVTglWcSV3Q==} 301 | peerDependencies: 302 | '@babel/core': ^7.0.0-0 303 | dependencies: 304 | '@babel/core': 7.23.9 305 | '@babel/helper-plugin-utils': 7.22.5 306 | dev: true 307 | 308 | /@babel/plugin-syntax-optional-chaining@7.8.3(@babel/core@7.23.9): 309 | resolution: {integrity: sha512-KoK9ErH1MBlCPxV0VANkXW2/dw4vlbGDrFgz8bmUsBGYkFRcbRwMh6cIJubdPrkxRwuGdtCk0v/wPTKbQgBjkg==} 310 | peerDependencies: 311 | '@babel/core': ^7.0.0-0 312 | dependencies: 313 | '@babel/core': 7.23.9 314 | '@babel/helper-plugin-utils': 7.22.5 315 | dev: true 316 | 317 | /@babel/plugin-syntax-top-level-await@7.14.5(@babel/core@7.23.9): 318 | resolution: {integrity: sha512-hx++upLv5U1rgYfwe1xBQUhRmU41NEvpUvrp8jkrSCdvGSnM5/qdRMtylJ6PG5OFkBaHkbTAKTnd3/YyESRHFw==} 319 | engines: {node: '>=6.9.0'} 320 | peerDependencies: 321 | '@babel/core': ^7.0.0-0 322 | dependencies: 323 | '@babel/core': 7.23.9 324 | '@babel/helper-plugin-utils': 7.22.5 325 | dev: true 326 | 327 | /@babel/plugin-syntax-typescript@7.23.3(@babel/core@7.23.9): 328 | resolution: {integrity: sha512-9EiNjVJOMwCO+43TqoTrgQ8jMwcAd0sWyXi9RPfIsLTj4R2MADDDQXELhffaUx/uJv2AYcxBgPwH6j4TIA4ytQ==} 329 | engines: {node: '>=6.9.0'} 330 | peerDependencies: 331 | '@babel/core': ^7.0.0-0 332 | dependencies: 333 | '@babel/core': 7.23.9 334 | '@babel/helper-plugin-utils': 7.22.5 335 | dev: true 336 | 337 | /@babel/template@7.23.9: 338 | resolution: {integrity: sha512-+xrD2BWLpvHKNmX2QbpdpsBaWnRxahMwJjO+KZk2JOElj5nSmKezyS1B4u+QbHMTX69t4ukm6hh9lsYQ7GHCKA==} 339 | engines: {node: '>=6.9.0'} 340 | dependencies: 341 | '@babel/code-frame': 7.23.5 342 | '@babel/parser': 7.23.9 343 | '@babel/types': 7.23.9 344 | dev: true 345 | 346 | /@babel/traverse@7.23.9: 347 | resolution: {integrity: sha512-I/4UJ9vs90OkBtY6iiiTORVMyIhJ4kAVmsKo9KFc8UOxMeUfi2hvtIBsET5u9GizXE6/GFSuKCTNfgCswuEjRg==} 348 | engines: {node: '>=6.9.0'} 349 | dependencies: 350 | '@babel/code-frame': 7.23.5 351 | '@babel/generator': 7.23.6 352 | '@babel/helper-environment-visitor': 7.22.20 353 | '@babel/helper-function-name': 7.23.0 354 | '@babel/helper-hoist-variables': 7.22.5 355 | '@babel/helper-split-export-declaration': 7.22.6 356 | '@babel/parser': 7.23.9 357 | '@babel/types': 7.23.9 358 | debug: 4.3.4 359 | globals: 11.12.0 360 | transitivePeerDependencies: 361 | - supports-color 362 | dev: true 363 | 364 | /@babel/types@7.23.9: 365 | resolution: {integrity: sha512-dQjSq/7HaSjRM43FFGnv5keM2HsxpmyV1PfaSVm0nzzjwwTmjOe6J4bC8e3+pTEIgHaHj+1ZlLThRJ2auc/w1Q==} 366 | engines: {node: '>=6.9.0'} 367 | dependencies: 368 | '@babel/helper-string-parser': 7.23.4 369 | '@babel/helper-validator-identifier': 7.22.20 370 | to-fast-properties: 2.0.0 371 | dev: true 372 | 373 | /@bcoe/v8-coverage@0.2.3: 374 | resolution: {integrity: sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==} 375 | dev: true 376 | 377 | /@istanbuljs/load-nyc-config@1.1.0: 378 | resolution: {integrity: sha512-VjeHSlIzpv/NyD3N0YuHfXOPDIixcA1q2ZV98wsMqcYlPmv2n3Yb2lYP9XMElnaFVXg5A7YLTeLu6V84uQDjmQ==} 379 | engines: {node: '>=8'} 380 | dependencies: 381 | camelcase: 5.3.1 382 | find-up: 4.1.0 383 | get-package-type: 0.1.0 384 | js-yaml: 3.14.1 385 | resolve-from: 5.0.0 386 | dev: true 387 | 388 | /@istanbuljs/schema@0.1.3: 389 | resolution: {integrity: sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA==} 390 | engines: {node: '>=8'} 391 | dev: true 392 | 393 | /@jest/console@29.7.0: 394 | resolution: {integrity: sha512-5Ni4CU7XHQi32IJ398EEP4RrB8eV09sXP2ROqD4bksHrnTree52PsxvX8tpL8LvTZ3pFzXyPbNQReSN41CAhOg==} 395 | engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} 396 | dependencies: 397 | '@jest/types': 29.6.3 398 | '@types/node': 20.11.16 399 | chalk: 4.1.2 400 | jest-message-util: 29.7.0 401 | jest-util: 29.7.0 402 | slash: 3.0.0 403 | dev: true 404 | 405 | /@jest/core@29.7.0: 406 | resolution: {integrity: sha512-n7aeXWKMnGtDA48y8TLWJPJmLmmZ642Ceo78cYWEpiD7FzDgmNDV/GCVRorPABdXLJZ/9wzzgZAlHjXjxDHGsg==} 407 | engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} 408 | peerDependencies: 409 | node-notifier: ^8.0.1 || ^9.0.0 || ^10.0.0 410 | peerDependenciesMeta: 411 | node-notifier: 412 | optional: true 413 | dependencies: 414 | '@jest/console': 29.7.0 415 | '@jest/reporters': 29.7.0 416 | '@jest/test-result': 29.7.0 417 | '@jest/transform': 29.7.0 418 | '@jest/types': 29.6.3 419 | '@types/node': 20.11.16 420 | ansi-escapes: 4.3.2 421 | chalk: 4.1.2 422 | ci-info: 3.9.0 423 | exit: 0.1.2 424 | graceful-fs: 4.2.11 425 | jest-changed-files: 29.7.0 426 | jest-config: 29.7.0(@types/node@20.11.16) 427 | jest-haste-map: 29.7.0 428 | jest-message-util: 29.7.0 429 | jest-regex-util: 29.6.3 430 | jest-resolve: 29.7.0 431 | jest-resolve-dependencies: 29.7.0 432 | jest-runner: 29.7.0 433 | jest-runtime: 29.7.0 434 | jest-snapshot: 29.7.0 435 | jest-util: 29.7.0 436 | jest-validate: 29.7.0 437 | jest-watcher: 29.7.0 438 | micromatch: 4.0.5 439 | pretty-format: 29.7.0 440 | slash: 3.0.0 441 | strip-ansi: 6.0.1 442 | transitivePeerDependencies: 443 | - babel-plugin-macros 444 | - supports-color 445 | - ts-node 446 | dev: true 447 | 448 | /@jest/environment@29.7.0: 449 | resolution: {integrity: sha512-aQIfHDq33ExsN4jP1NWGXhxgQ/wixs60gDiKO+XVMd8Mn0NWPWgc34ZQDTb2jKaUWQ7MuwoitXAsN2XVXNMpAw==} 450 | engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} 451 | dependencies: 452 | '@jest/fake-timers': 29.7.0 453 | '@jest/types': 29.6.3 454 | '@types/node': 20.11.16 455 | jest-mock: 29.7.0 456 | dev: true 457 | 458 | /@jest/expect-utils@29.7.0: 459 | resolution: {integrity: sha512-GlsNBWiFQFCVi9QVSx7f5AgMeLxe9YCCs5PuP2O2LdjDAA8Jh9eX7lA1Jq/xdXw3Wb3hyvlFNfZIfcRetSzYcA==} 460 | engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} 461 | dependencies: 462 | jest-get-type: 29.6.3 463 | dev: true 464 | 465 | /@jest/expect@29.7.0: 466 | resolution: {integrity: sha512-8uMeAMycttpva3P1lBHB8VciS9V0XAr3GymPpipdyQXbBcuhkLQOSe8E/p92RyAdToS6ZD1tFkX+CkhoECE0dQ==} 467 | engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} 468 | dependencies: 469 | expect: 29.7.0 470 | jest-snapshot: 29.7.0 471 | transitivePeerDependencies: 472 | - supports-color 473 | dev: true 474 | 475 | /@jest/fake-timers@29.7.0: 476 | resolution: {integrity: sha512-q4DH1Ha4TTFPdxLsqDXK1d3+ioSL7yL5oCMJZgDYm6i+6CygW5E5xVr/D1HdsGxjt1ZWSfUAs9OxSB/BNelWrQ==} 477 | engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} 478 | dependencies: 479 | '@jest/types': 29.6.3 480 | '@sinonjs/fake-timers': 10.3.0 481 | '@types/node': 20.11.16 482 | jest-message-util: 29.7.0 483 | jest-mock: 29.7.0 484 | jest-util: 29.7.0 485 | dev: true 486 | 487 | /@jest/globals@29.7.0: 488 | resolution: {integrity: sha512-mpiz3dutLbkW2MNFubUGUEVLkTGiqW6yLVTA+JbP6fI6J5iL9Y0Nlg8k95pcF8ctKwCS7WVxteBs29hhfAotzQ==} 489 | engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} 490 | dependencies: 491 | '@jest/environment': 29.7.0 492 | '@jest/expect': 29.7.0 493 | '@jest/types': 29.6.3 494 | jest-mock: 29.7.0 495 | transitivePeerDependencies: 496 | - supports-color 497 | dev: true 498 | 499 | /@jest/reporters@29.7.0: 500 | resolution: {integrity: sha512-DApq0KJbJOEzAFYjHADNNxAE3KbhxQB1y5Kplb5Waqw6zVbuWatSnMjE5gs8FUgEPmNsnZA3NCWl9NG0ia04Pg==} 501 | engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} 502 | peerDependencies: 503 | node-notifier: ^8.0.1 || ^9.0.0 || ^10.0.0 504 | peerDependenciesMeta: 505 | node-notifier: 506 | optional: true 507 | dependencies: 508 | '@bcoe/v8-coverage': 0.2.3 509 | '@jest/console': 29.7.0 510 | '@jest/test-result': 29.7.0 511 | '@jest/transform': 29.7.0 512 | '@jest/types': 29.6.3 513 | '@jridgewell/trace-mapping': 0.3.22 514 | '@types/node': 20.11.16 515 | chalk: 4.1.2 516 | collect-v8-coverage: 1.0.2 517 | exit: 0.1.2 518 | glob: 7.2.3 519 | graceful-fs: 4.2.11 520 | istanbul-lib-coverage: 3.2.2 521 | istanbul-lib-instrument: 6.0.1 522 | istanbul-lib-report: 3.0.1 523 | istanbul-lib-source-maps: 4.0.1 524 | istanbul-reports: 3.1.6 525 | jest-message-util: 29.7.0 526 | jest-util: 29.7.0 527 | jest-worker: 29.7.0 528 | slash: 3.0.0 529 | string-length: 4.0.2 530 | strip-ansi: 6.0.1 531 | v8-to-istanbul: 9.2.0 532 | transitivePeerDependencies: 533 | - supports-color 534 | dev: true 535 | 536 | /@jest/schemas@29.6.3: 537 | resolution: {integrity: sha512-mo5j5X+jIZmJQveBKeS/clAueipV7KgiX1vMgCxam1RNYiqE1w62n0/tJJnHtjW8ZHcQco5gY85jA3mi0L+nSA==} 538 | engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} 539 | dependencies: 540 | '@sinclair/typebox': 0.27.8 541 | dev: true 542 | 543 | /@jest/source-map@29.6.3: 544 | resolution: {integrity: sha512-MHjT95QuipcPrpLM+8JMSzFx6eHp5Bm+4XeFDJlwsvVBjmKNiIAvasGK2fxz2WbGRlnvqehFbh07MMa7n3YJnw==} 545 | engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} 546 | dependencies: 547 | '@jridgewell/trace-mapping': 0.3.22 548 | callsites: 3.1.0 549 | graceful-fs: 4.2.11 550 | dev: true 551 | 552 | /@jest/test-result@29.7.0: 553 | resolution: {integrity: sha512-Fdx+tv6x1zlkJPcWXmMDAG2HBnaR9XPSd5aDWQVsfrZmLVT3lU1cwyxLgRmXR9yrq4NBoEm9BMsfgFzTQAbJYA==} 554 | engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} 555 | dependencies: 556 | '@jest/console': 29.7.0 557 | '@jest/types': 29.6.3 558 | '@types/istanbul-lib-coverage': 2.0.6 559 | collect-v8-coverage: 1.0.2 560 | dev: true 561 | 562 | /@jest/test-sequencer@29.7.0: 563 | resolution: {integrity: sha512-GQwJ5WZVrKnOJuiYiAF52UNUJXgTZx1NHjFSEB0qEMmSZKAkdMoIzw/Cj6x6NF4AvV23AUqDpFzQkN/eYCYTxw==} 564 | engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} 565 | dependencies: 566 | '@jest/test-result': 29.7.0 567 | graceful-fs: 4.2.11 568 | jest-haste-map: 29.7.0 569 | slash: 3.0.0 570 | dev: true 571 | 572 | /@jest/transform@29.7.0: 573 | resolution: {integrity: sha512-ok/BTPFzFKVMwO5eOHRrvnBVHdRy9IrsrW1GpMaQ9MCnilNLXQKmAX8s1YXDFaai9xJpac2ySzV0YeRRECr2Vw==} 574 | engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} 575 | dependencies: 576 | '@babel/core': 7.23.9 577 | '@jest/types': 29.6.3 578 | '@jridgewell/trace-mapping': 0.3.22 579 | babel-plugin-istanbul: 6.1.1 580 | chalk: 4.1.2 581 | convert-source-map: 2.0.0 582 | fast-json-stable-stringify: 2.1.0 583 | graceful-fs: 4.2.11 584 | jest-haste-map: 29.7.0 585 | jest-regex-util: 29.6.3 586 | jest-util: 29.7.0 587 | micromatch: 4.0.5 588 | pirates: 4.0.6 589 | slash: 3.0.0 590 | write-file-atomic: 4.0.2 591 | transitivePeerDependencies: 592 | - supports-color 593 | dev: true 594 | 595 | /@jest/types@29.6.3: 596 | resolution: {integrity: sha512-u3UPsIilWKOM3F9CXtrG8LEJmNxwoCQC/XVj4IKYXvvpx7QIi/Kg1LI5uDmDpKlac62NUtX7eLjRh+jVZcLOzw==} 597 | engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} 598 | dependencies: 599 | '@jest/schemas': 29.6.3 600 | '@types/istanbul-lib-coverage': 2.0.6 601 | '@types/istanbul-reports': 3.0.4 602 | '@types/node': 20.11.16 603 | '@types/yargs': 17.0.32 604 | chalk: 4.1.2 605 | dev: true 606 | 607 | /@jridgewell/gen-mapping@0.3.3: 608 | resolution: {integrity: sha512-HLhSWOLRi875zjjMG/r+Nv0oCW8umGb0BgEhyX3dDX3egwZtB8PqLnjz3yedt8R5StBrzcg4aBpnh8UA9D1BoQ==} 609 | engines: {node: '>=6.0.0'} 610 | dependencies: 611 | '@jridgewell/set-array': 1.1.2 612 | '@jridgewell/sourcemap-codec': 1.4.15 613 | '@jridgewell/trace-mapping': 0.3.22 614 | dev: true 615 | 616 | /@jridgewell/resolve-uri@3.1.1: 617 | resolution: {integrity: sha512-dSYZh7HhCDtCKm4QakX0xFpsRDqjjtZf/kjI/v3T3Nwt5r8/qz/M19F9ySyOqU94SXBmeG9ttTul+YnR4LOxFA==} 618 | engines: {node: '>=6.0.0'} 619 | dev: true 620 | 621 | /@jridgewell/set-array@1.1.2: 622 | resolution: {integrity: sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw==} 623 | engines: {node: '>=6.0.0'} 624 | dev: true 625 | 626 | /@jridgewell/sourcemap-codec@1.4.15: 627 | resolution: {integrity: sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==} 628 | dev: true 629 | 630 | /@jridgewell/trace-mapping@0.3.22: 631 | resolution: {integrity: sha512-Wf963MzWtA2sjrNt+g18IAln9lKnlRp+K2eH4jjIoF1wYeq3aMREpG09xhlhdzS0EjwU7qmUJYangWa+151vZw==} 632 | dependencies: 633 | '@jridgewell/resolve-uri': 3.1.1 634 | '@jridgewell/sourcemap-codec': 1.4.15 635 | dev: true 636 | 637 | /@sinclair/typebox@0.27.8: 638 | resolution: {integrity: sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA==} 639 | dev: true 640 | 641 | /@sinonjs/commons@3.0.1: 642 | resolution: {integrity: sha512-K3mCHKQ9sVh8o1C9cxkwxaOmXoAMlDxC1mYyHrjqOWEcBjYr76t96zL2zlj5dUGZ3HSw240X1qgH3Mjf1yJWpQ==} 643 | dependencies: 644 | type-detect: 4.0.8 645 | dev: true 646 | 647 | /@sinonjs/fake-timers@10.3.0: 648 | resolution: {integrity: sha512-V4BG07kuYSUkTCSBHG8G8TNhM+F19jXFWnQtzj+we8DrkpSBCee9Z3Ms8yiGer/dlmhe35/Xdgyo3/0rQKg7YA==} 649 | dependencies: 650 | '@sinonjs/commons': 3.0.1 651 | dev: true 652 | 653 | /@types/babel__core@7.20.5: 654 | resolution: {integrity: sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==} 655 | dependencies: 656 | '@babel/parser': 7.23.9 657 | '@babel/types': 7.23.9 658 | '@types/babel__generator': 7.6.8 659 | '@types/babel__template': 7.4.4 660 | '@types/babel__traverse': 7.20.5 661 | dev: true 662 | 663 | /@types/babel__generator@7.6.8: 664 | resolution: {integrity: sha512-ASsj+tpEDsEiFr1arWrlN6V3mdfjRMZt6LtK/Vp/kreFLnr5QH5+DhvD5nINYZXzwJvXeGq+05iUXcAzVrqWtw==} 665 | dependencies: 666 | '@babel/types': 7.23.9 667 | dev: true 668 | 669 | /@types/babel__template@7.4.4: 670 | resolution: {integrity: sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A==} 671 | dependencies: 672 | '@babel/parser': 7.23.9 673 | '@babel/types': 7.23.9 674 | dev: true 675 | 676 | /@types/babel__traverse@7.20.5: 677 | resolution: {integrity: sha512-WXCyOcRtH37HAUkpXhUduaxdm82b4GSlyTqajXviN4EfiuPgNYR109xMCKvpl6zPIpua0DGlMEDCq+g8EdoheQ==} 678 | dependencies: 679 | '@babel/types': 7.23.9 680 | dev: true 681 | 682 | /@types/body-parser@1.19.5: 683 | resolution: {integrity: sha512-fB3Zu92ucau0iQ0JMCFQE7b/dv8Ot07NI3KaZIkIUNXq82k4eBAqUaneXfleGY9JWskeS9y+u0nXMyspcuQrCg==} 684 | dependencies: 685 | '@types/connect': 3.4.38 686 | '@types/node': 20.11.16 687 | dev: true 688 | 689 | /@types/connect@3.4.38: 690 | resolution: {integrity: sha512-K6uROf1LD88uDQqJCktA4yzL1YYAK6NgfsI0v/mTgyPKWsX1CnJ0XPSDhViejru1GcRkLWb8RlzFYJRqGUbaug==} 691 | dependencies: 692 | '@types/node': 20.11.16 693 | dev: true 694 | 695 | /@types/cookiejar@2.1.5: 696 | resolution: {integrity: sha512-he+DHOWReW0nghN24E1WUqM0efK4kI9oTqDm6XmK8ZPe2djZ90BSNdGnIyCLzCPw7/pogPlGbzI2wHGGmi4O/Q==} 697 | dev: true 698 | 699 | /@types/express-serve-static-core@4.17.43: 700 | resolution: {integrity: sha512-oaYtiBirUOPQGSWNGPWnzyAFJ0BP3cwvN4oWZQY+zUBwpVIGsKUkpBpSztp74drYcjavs7SKFZ4DX1V2QeN8rg==} 701 | dependencies: 702 | '@types/node': 20.11.16 703 | '@types/qs': 6.9.11 704 | '@types/range-parser': 1.2.7 705 | '@types/send': 0.17.4 706 | dev: true 707 | 708 | /@types/express@4.17.21: 709 | resolution: {integrity: sha512-ejlPM315qwLpaQlQDTjPdsUFSc6ZsP4AN6AlWnogPjQ7CVi7PYF3YVz+CY3jE2pwYf7E/7HlDAN0rV2GxTG0HQ==} 710 | dependencies: 711 | '@types/body-parser': 1.19.5 712 | '@types/express-serve-static-core': 4.17.43 713 | '@types/qs': 6.9.11 714 | '@types/serve-static': 1.15.5 715 | dev: true 716 | 717 | /@types/graceful-fs@4.1.9: 718 | resolution: {integrity: sha512-olP3sd1qOEe5dXTSaFvQG+02VdRXcdytWLAZsAq1PecU8uqQAhkrnbli7DagjtXKW/Bl7YJbUsa8MPcuc8LHEQ==} 719 | dependencies: 720 | '@types/node': 20.11.16 721 | dev: true 722 | 723 | /@types/http-errors@2.0.4: 724 | resolution: {integrity: sha512-D0CFMMtydbJAegzOyHjtiKPLlvnm3iTZyZRSZoLq2mRhDdmLfIWOCYPfQJ4cu2erKghU++QvjcUjp/5h7hESpA==} 725 | dev: true 726 | 727 | /@types/istanbul-lib-coverage@2.0.6: 728 | resolution: {integrity: sha512-2QF/t/auWm0lsy8XtKVPG19v3sSOQlJe/YHZgfjb/KBBHOGSV+J2q/S671rcq9uTBrLAXmZpqJiaQbMT+zNU1w==} 729 | dev: true 730 | 731 | /@types/istanbul-lib-report@3.0.3: 732 | resolution: {integrity: sha512-NQn7AHQnk/RSLOxrBbGyJM/aVQ+pjj5HCgasFxc0K/KhoATfQ/47AyUl15I2yBUpihjmas+a+VJBOqecrFH+uA==} 733 | dependencies: 734 | '@types/istanbul-lib-coverage': 2.0.6 735 | dev: true 736 | 737 | /@types/istanbul-reports@3.0.4: 738 | resolution: {integrity: sha512-pk2B1NWalF9toCRu6gjBzR69syFjP4Od8WRAX+0mmf9lAjCRicLOWc+ZrxZHx/0XRjotgkF9t6iaMJ+aXcOdZQ==} 739 | dependencies: 740 | '@types/istanbul-lib-report': 3.0.3 741 | dev: true 742 | 743 | /@types/jest@29.5.12: 744 | resolution: {integrity: sha512-eDC8bTvT/QhYdxJAulQikueigY5AsdBRH2yDKW3yveW7svY3+DzN84/2NUgkw10RTiJbWqZrTtoGVdYlvFJdLw==} 745 | dependencies: 746 | expect: 29.7.0 747 | pretty-format: 29.7.0 748 | dev: true 749 | 750 | /@types/methods@1.1.4: 751 | resolution: {integrity: sha512-ymXWVrDiCxTBE3+RIrrP533E70eA+9qu7zdWoHuOmGujkYtzf4HQF96b8nwHLqhuf4ykX61IGRIB38CC6/sImQ==} 752 | dev: true 753 | 754 | /@types/mime@1.3.5: 755 | resolution: {integrity: sha512-/pyBZWSLD2n0dcHE3hq8s8ZvcETHtEuF+3E7XVt0Ig2nvsVQXdghHVcEkIWjy9A0wKfTn97a/PSDYohKIlnP/w==} 756 | dev: true 757 | 758 | /@types/mime@3.0.4: 759 | resolution: {integrity: sha512-iJt33IQnVRkqeqC7PzBHPTC6fDlRNRW8vjrgqtScAhrmMwe8c4Eo7+fUGTa+XdWrpEgpyKWMYmi2dIwMAYRzPw==} 760 | dev: true 761 | 762 | /@types/node@20.11.16: 763 | resolution: {integrity: sha512-gKb0enTmRCzXSSUJDq6/sPcqrfCv2mkkG6Jt/clpn5eiCbKTY+SgZUxo+p8ZKMof5dCp9vHQUAB7wOUTod22wQ==} 764 | dependencies: 765 | undici-types: 5.26.5 766 | dev: true 767 | 768 | /@types/qs@6.9.11: 769 | resolution: {integrity: sha512-oGk0gmhnEJK4Yyk+oI7EfXsLayXatCWPHary1MtcmbAifkobT9cM9yutG/hZKIseOU0MqbIwQ/u2nn/Gb+ltuQ==} 770 | dev: true 771 | 772 | /@types/range-parser@1.2.7: 773 | resolution: {integrity: sha512-hKormJbkJqzQGhziax5PItDUTMAM9uE2XXQmM37dyd4hVM+5aVl7oVxMVUiVQn2oCQFN/LKCZdvSM0pFRqbSmQ==} 774 | dev: true 775 | 776 | /@types/send@0.17.4: 777 | resolution: {integrity: sha512-x2EM6TJOybec7c52BX0ZspPodMsQUd5L6PRwOunVyVUhXiBSKf3AezDL8Dgvgt5o0UfKNfuA0eMLr2wLT4AiBA==} 778 | dependencies: 779 | '@types/mime': 1.3.5 780 | '@types/node': 20.11.16 781 | dev: true 782 | 783 | /@types/serve-static@1.15.5: 784 | resolution: {integrity: sha512-PDRk21MnK70hja/YF8AHfC7yIsiQHn1rcXx7ijCFBX/k+XQJhQT/gw3xekXKJvx+5SXaMMS8oqQy09Mzvz2TuQ==} 785 | dependencies: 786 | '@types/http-errors': 2.0.4 787 | '@types/mime': 3.0.4 788 | '@types/node': 20.11.16 789 | dev: true 790 | 791 | /@types/stack-utils@2.0.3: 792 | resolution: {integrity: sha512-9aEbYZ3TbYMznPdcdr3SmIrLXwC/AKZXQeCf9Pgao5CKb8CyHuEX5jzWPTkvregvhRJHcpRO6BFoGW9ycaOkYw==} 793 | dev: true 794 | 795 | /@types/superagent@8.1.3: 796 | resolution: {integrity: sha512-R/CfN6w2XsixLb1Ii8INfn+BT9sGPvw74OavfkW4SwY+jeUcAwLZv2+bXLJkndnimxjEBm0RPHgcjW9pLCa8cw==} 797 | dependencies: 798 | '@types/cookiejar': 2.1.5 799 | '@types/methods': 1.1.4 800 | '@types/node': 20.11.16 801 | dev: true 802 | 803 | /@types/supertest@6.0.2: 804 | resolution: {integrity: sha512-137ypx2lk/wTQbW6An6safu9hXmajAifU/s7szAHLN/FeIm5w7yR0Wkl9fdJMRSHwOn4HLAI0DaB2TOORuhPDg==} 805 | dependencies: 806 | '@types/methods': 1.1.4 807 | '@types/superagent': 8.1.3 808 | dev: true 809 | 810 | /@types/yargs-parser@21.0.3: 811 | resolution: {integrity: sha512-I4q9QU9MQv4oEOz4tAHJtNz1cwuLxn2F3xcc2iV5WdqLPpUnj30aUuxt1mAxYTG+oe8CZMV/+6rU4S4gRDzqtQ==} 812 | dev: true 813 | 814 | /@types/yargs@17.0.32: 815 | resolution: {integrity: sha512-xQ67Yc/laOG5uMfX/093MRlGGCIBzZMarVa+gfNKJxWAIgykYpVGkBdbqEzGDDfCrVUj6Hiff4mTZ5BA6TmAog==} 816 | dependencies: 817 | '@types/yargs-parser': 21.0.3 818 | dev: true 819 | 820 | /accepts@1.3.8: 821 | resolution: {integrity: sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==} 822 | engines: {node: '>= 0.6'} 823 | dependencies: 824 | mime-types: 2.1.35 825 | negotiator: 0.6.3 826 | dev: false 827 | 828 | /ansi-escapes@4.3.2: 829 | resolution: {integrity: sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==} 830 | engines: {node: '>=8'} 831 | dependencies: 832 | type-fest: 0.21.3 833 | dev: true 834 | 835 | /ansi-regex@5.0.1: 836 | resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==} 837 | engines: {node: '>=8'} 838 | dev: true 839 | 840 | /ansi-styles@3.2.1: 841 | resolution: {integrity: sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==} 842 | engines: {node: '>=4'} 843 | dependencies: 844 | color-convert: 1.9.3 845 | dev: true 846 | 847 | /ansi-styles@4.3.0: 848 | resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==} 849 | engines: {node: '>=8'} 850 | dependencies: 851 | color-convert: 2.0.1 852 | dev: true 853 | 854 | /ansi-styles@5.2.0: 855 | resolution: {integrity: sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==} 856 | engines: {node: '>=10'} 857 | dev: true 858 | 859 | /anymatch@3.1.3: 860 | resolution: {integrity: sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==} 861 | engines: {node: '>= 8'} 862 | dependencies: 863 | normalize-path: 3.0.0 864 | picomatch: 2.3.1 865 | dev: true 866 | 867 | /argparse@1.0.10: 868 | resolution: {integrity: sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==} 869 | dependencies: 870 | sprintf-js: 1.0.3 871 | dev: true 872 | 873 | /array-flatten@1.1.1: 874 | resolution: {integrity: sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==} 875 | dev: false 876 | 877 | /asap@2.0.6: 878 | resolution: {integrity: sha512-BSHWgDSAiKs50o2Re8ppvp3seVHXSRM44cdSsT9FfNEUUZLOGWVCsiWaRPWM1Znn+mqZ1OfVZ3z3DWEzSp7hRA==} 879 | dev: true 880 | 881 | /asynckit@0.4.0: 882 | resolution: {integrity: sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==} 883 | dev: true 884 | 885 | /babel-jest@29.7.0(@babel/core@7.23.9): 886 | resolution: {integrity: sha512-BrvGY3xZSwEcCzKvKsCi2GgHqDqsYkOP4/by5xCgIwGXQxIEh+8ew3gmrE1y7XRR6LHZIj6yLYnUi/mm2KXKBg==} 887 | engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} 888 | peerDependencies: 889 | '@babel/core': ^7.8.0 890 | dependencies: 891 | '@babel/core': 7.23.9 892 | '@jest/transform': 29.7.0 893 | '@types/babel__core': 7.20.5 894 | babel-plugin-istanbul: 6.1.1 895 | babel-preset-jest: 29.6.3(@babel/core@7.23.9) 896 | chalk: 4.1.2 897 | graceful-fs: 4.2.11 898 | slash: 3.0.0 899 | transitivePeerDependencies: 900 | - supports-color 901 | dev: true 902 | 903 | /babel-plugin-istanbul@6.1.1: 904 | resolution: {integrity: sha512-Y1IQok9821cC9onCx5otgFfRm7Lm+I+wwxOx738M/WLPZ9Q42m4IG5W0FNX8WLL2gYMZo3JkuXIH2DOpWM+qwA==} 905 | engines: {node: '>=8'} 906 | dependencies: 907 | '@babel/helper-plugin-utils': 7.22.5 908 | '@istanbuljs/load-nyc-config': 1.1.0 909 | '@istanbuljs/schema': 0.1.3 910 | istanbul-lib-instrument: 5.2.1 911 | test-exclude: 6.0.0 912 | transitivePeerDependencies: 913 | - supports-color 914 | dev: true 915 | 916 | /babel-plugin-jest-hoist@29.6.3: 917 | resolution: {integrity: sha512-ESAc/RJvGTFEzRwOTT4+lNDk/GNHMkKbNzsvT0qKRfDyyYTskxB5rnU2njIDYVxXCBHHEI1c0YwHob3WaYujOg==} 918 | engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} 919 | dependencies: 920 | '@babel/template': 7.23.9 921 | '@babel/types': 7.23.9 922 | '@types/babel__core': 7.20.5 923 | '@types/babel__traverse': 7.20.5 924 | dev: true 925 | 926 | /babel-preset-current-node-syntax@1.0.1(@babel/core@7.23.9): 927 | resolution: {integrity: sha512-M7LQ0bxarkxQoN+vz5aJPsLBn77n8QgTFmo8WK0/44auK2xlCXrYcUxHFxgU7qW5Yzw/CjmLRK2uJzaCd7LvqQ==} 928 | peerDependencies: 929 | '@babel/core': ^7.0.0 930 | dependencies: 931 | '@babel/core': 7.23.9 932 | '@babel/plugin-syntax-async-generators': 7.8.4(@babel/core@7.23.9) 933 | '@babel/plugin-syntax-bigint': 7.8.3(@babel/core@7.23.9) 934 | '@babel/plugin-syntax-class-properties': 7.12.13(@babel/core@7.23.9) 935 | '@babel/plugin-syntax-import-meta': 7.10.4(@babel/core@7.23.9) 936 | '@babel/plugin-syntax-json-strings': 7.8.3(@babel/core@7.23.9) 937 | '@babel/plugin-syntax-logical-assignment-operators': 7.10.4(@babel/core@7.23.9) 938 | '@babel/plugin-syntax-nullish-coalescing-operator': 7.8.3(@babel/core@7.23.9) 939 | '@babel/plugin-syntax-numeric-separator': 7.10.4(@babel/core@7.23.9) 940 | '@babel/plugin-syntax-object-rest-spread': 7.8.3(@babel/core@7.23.9) 941 | '@babel/plugin-syntax-optional-catch-binding': 7.8.3(@babel/core@7.23.9) 942 | '@babel/plugin-syntax-optional-chaining': 7.8.3(@babel/core@7.23.9) 943 | '@babel/plugin-syntax-top-level-await': 7.14.5(@babel/core@7.23.9) 944 | dev: true 945 | 946 | /babel-preset-jest@29.6.3(@babel/core@7.23.9): 947 | resolution: {integrity: sha512-0B3bhxR6snWXJZtR/RliHTDPRgn1sNHOR0yVtq/IiQFyuOVjFS+wuio/R4gSNkyYmKmJB4wGZv2NZanmKmTnNA==} 948 | engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} 949 | peerDependencies: 950 | '@babel/core': ^7.0.0 951 | dependencies: 952 | '@babel/core': 7.23.9 953 | babel-plugin-jest-hoist: 29.6.3 954 | babel-preset-current-node-syntax: 1.0.1(@babel/core@7.23.9) 955 | dev: true 956 | 957 | /balanced-match@1.0.2: 958 | resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} 959 | dev: true 960 | 961 | /body-parser@1.20.1: 962 | resolution: {integrity: sha512-jWi7abTbYwajOytWCQc37VulmWiRae5RyTpaCyDcS5/lMdtwSz5lOpDE67srw/HYe35f1z3fDQw+3txg7gNtWw==} 963 | engines: {node: '>= 0.8', npm: 1.2.8000 || >= 1.4.16} 964 | dependencies: 965 | bytes: 3.1.2 966 | content-type: 1.0.5 967 | debug: 2.6.9 968 | depd: 2.0.0 969 | destroy: 1.2.0 970 | http-errors: 2.0.0 971 | iconv-lite: 0.4.24 972 | on-finished: 2.4.1 973 | qs: 6.11.0 974 | raw-body: 2.5.1 975 | type-is: 1.6.18 976 | unpipe: 1.0.0 977 | transitivePeerDependencies: 978 | - supports-color 979 | dev: false 980 | 981 | /brace-expansion@1.1.11: 982 | resolution: {integrity: sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==} 983 | dependencies: 984 | balanced-match: 1.0.2 985 | concat-map: 0.0.1 986 | dev: true 987 | 988 | /braces@3.0.2: 989 | resolution: {integrity: sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==} 990 | engines: {node: '>=8'} 991 | dependencies: 992 | fill-range: 7.0.1 993 | dev: true 994 | 995 | /browserslist@4.22.3: 996 | resolution: {integrity: sha512-UAp55yfwNv0klWNapjs/ktHoguxuQNGnOzxYmfnXIS+8AsRDZkSDxg7R1AX3GKzn078SBI5dzwzj/Yx0Or0e3A==} 997 | engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7} 998 | hasBin: true 999 | dependencies: 1000 | caniuse-lite: 1.0.30001583 1001 | electron-to-chromium: 1.4.656 1002 | node-releases: 2.0.14 1003 | update-browserslist-db: 1.0.13(browserslist@4.22.3) 1004 | dev: true 1005 | 1006 | /bs-logger@0.2.6: 1007 | resolution: {integrity: sha512-pd8DCoxmbgc7hyPKOvxtqNcjYoOsABPQdcCUjGp3d42VR2CX1ORhk2A87oqqu5R1kk+76nsxZupkmyd+MVtCog==} 1008 | engines: {node: '>= 6'} 1009 | dependencies: 1010 | fast-json-stable-stringify: 2.1.0 1011 | dev: true 1012 | 1013 | /bser@2.1.1: 1014 | resolution: {integrity: sha512-gQxTNE/GAfIIrmHLUE3oJyp5FO6HRBfhjnw4/wMmA63ZGDJnWBmgY/lyQBpnDUkGmAhbSe39tx2d/iTOAfglwQ==} 1015 | dependencies: 1016 | node-int64: 0.4.0 1017 | dev: true 1018 | 1019 | /buffer-from@1.1.2: 1020 | resolution: {integrity: sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==} 1021 | dev: true 1022 | 1023 | /bytes@3.1.2: 1024 | resolution: {integrity: sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==} 1025 | engines: {node: '>= 0.8'} 1026 | dev: false 1027 | 1028 | /call-bind@1.0.5: 1029 | resolution: {integrity: sha512-C3nQxfFZxFRVoJoGKKI8y3MOEo129NQ+FgQ08iye+Mk4zNZZGdjfs06bVTr+DBSlA66Q2VEcMki/cUCP4SercQ==} 1030 | dependencies: 1031 | function-bind: 1.1.2 1032 | get-intrinsic: 1.2.2 1033 | set-function-length: 1.2.0 1034 | 1035 | /callsites@3.1.0: 1036 | resolution: {integrity: sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==} 1037 | engines: {node: '>=6'} 1038 | dev: true 1039 | 1040 | /camelcase@5.3.1: 1041 | resolution: {integrity: sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==} 1042 | engines: {node: '>=6'} 1043 | dev: true 1044 | 1045 | /camelcase@6.3.0: 1046 | resolution: {integrity: sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==} 1047 | engines: {node: '>=10'} 1048 | dev: true 1049 | 1050 | /caniuse-lite@1.0.30001583: 1051 | resolution: {integrity: sha512-acWTYaha8xfhA/Du/z4sNZjHUWjkiuoAi2LM+T/aL+kemKQgPT1xBb/YKjlQ0Qo8gvbHsGNplrEJ+9G3gL7i4Q==} 1052 | dev: true 1053 | 1054 | /chalk@2.4.2: 1055 | resolution: {integrity: sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==} 1056 | engines: {node: '>=4'} 1057 | dependencies: 1058 | ansi-styles: 3.2.1 1059 | escape-string-regexp: 1.0.5 1060 | supports-color: 5.5.0 1061 | dev: true 1062 | 1063 | /chalk@4.1.2: 1064 | resolution: {integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==} 1065 | engines: {node: '>=10'} 1066 | dependencies: 1067 | ansi-styles: 4.3.0 1068 | supports-color: 7.2.0 1069 | dev: true 1070 | 1071 | /char-regex@1.0.2: 1072 | resolution: {integrity: sha512-kWWXztvZ5SBQV+eRgKFeh8q5sLuZY2+8WUIzlxWVTg+oGwY14qylx1KbKzHd8P6ZYkAg0xyIDU9JMHhyJMZ1jw==} 1073 | engines: {node: '>=10'} 1074 | dev: true 1075 | 1076 | /ci-info@3.9.0: 1077 | resolution: {integrity: sha512-NIxF55hv4nSqQswkAeiOi1r83xy8JldOFDTWiug55KBu9Jnblncd2U6ViHmYgHf01TPZS77NJBhBMKdWj9HQMQ==} 1078 | engines: {node: '>=8'} 1079 | dev: true 1080 | 1081 | /cjs-module-lexer@1.2.3: 1082 | resolution: {integrity: sha512-0TNiGstbQmCFwt4akjjBg5pLRTSyj/PkWQ1ZoO2zntmg9yLqSRxwEa4iCfQLGjqhiqBfOJa7W/E8wfGrTDmlZQ==} 1083 | dev: true 1084 | 1085 | /cliui@8.0.1: 1086 | resolution: {integrity: sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==} 1087 | engines: {node: '>=12'} 1088 | dependencies: 1089 | string-width: 4.2.3 1090 | strip-ansi: 6.0.1 1091 | wrap-ansi: 7.0.0 1092 | dev: true 1093 | 1094 | /co@4.6.0: 1095 | resolution: {integrity: sha512-QVb0dM5HvG+uaxitm8wONl7jltx8dqhfU33DcqtOZcLSVIKSDDLDi7+0LbAKiyI8hD9u42m2YxXSkMGWThaecQ==} 1096 | engines: {iojs: '>= 1.0.0', node: '>= 0.12.0'} 1097 | dev: true 1098 | 1099 | /collect-v8-coverage@1.0.2: 1100 | resolution: {integrity: sha512-lHl4d5/ONEbLlJvaJNtsF/Lz+WvB07u2ycqTYbdrq7UypDXailES4valYb2eWiJFxZlVmpGekfqoxQhzyFdT4Q==} 1101 | dev: true 1102 | 1103 | /color-convert@1.9.3: 1104 | resolution: {integrity: sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==} 1105 | dependencies: 1106 | color-name: 1.1.3 1107 | dev: true 1108 | 1109 | /color-convert@2.0.1: 1110 | resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==} 1111 | engines: {node: '>=7.0.0'} 1112 | dependencies: 1113 | color-name: 1.1.4 1114 | dev: true 1115 | 1116 | /color-name@1.1.3: 1117 | resolution: {integrity: sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==} 1118 | dev: true 1119 | 1120 | /color-name@1.1.4: 1121 | resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==} 1122 | dev: true 1123 | 1124 | /combined-stream@1.0.8: 1125 | resolution: {integrity: sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==} 1126 | engines: {node: '>= 0.8'} 1127 | dependencies: 1128 | delayed-stream: 1.0.0 1129 | dev: true 1130 | 1131 | /component-emitter@1.3.1: 1132 | resolution: {integrity: sha512-T0+barUSQRTUQASh8bx02dl+DhF54GtIDY13Y3m9oWTklKbb3Wv974meRpeZ3lp1JpLVECWWNHC4vaG2XHXouQ==} 1133 | dev: true 1134 | 1135 | /concat-map@0.0.1: 1136 | resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==} 1137 | dev: true 1138 | 1139 | /content-disposition@0.5.4: 1140 | resolution: {integrity: sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==} 1141 | engines: {node: '>= 0.6'} 1142 | dependencies: 1143 | safe-buffer: 5.2.1 1144 | dev: false 1145 | 1146 | /content-type@1.0.5: 1147 | resolution: {integrity: sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==} 1148 | engines: {node: '>= 0.6'} 1149 | dev: false 1150 | 1151 | /convert-source-map@2.0.0: 1152 | resolution: {integrity: sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==} 1153 | dev: true 1154 | 1155 | /cookie-signature@1.0.6: 1156 | resolution: {integrity: sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==} 1157 | dev: false 1158 | 1159 | /cookie@0.5.0: 1160 | resolution: {integrity: sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw==} 1161 | engines: {node: '>= 0.6'} 1162 | dev: false 1163 | 1164 | /cookiejar@2.1.4: 1165 | resolution: {integrity: sha512-LDx6oHrK+PhzLKJU9j5S7/Y3jM/mUHvD/DeI1WQmJn652iPC5Y4TBzC9l+5OMOXlyTTA+SmVUPm0HQUwpD5Jqw==} 1166 | dev: true 1167 | 1168 | /create-jest@29.7.0: 1169 | resolution: {integrity: sha512-Adz2bdH0Vq3F53KEMJOoftQFutWCukm6J24wbPWRO4k1kMY7gS7ds/uoJkNuV8wDCtWWnuwGcJwpWcih+zEW1Q==} 1170 | engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} 1171 | hasBin: true 1172 | dependencies: 1173 | '@jest/types': 29.6.3 1174 | chalk: 4.1.2 1175 | exit: 0.1.2 1176 | graceful-fs: 4.2.11 1177 | jest-config: 29.7.0(@types/node@20.11.16) 1178 | jest-util: 29.7.0 1179 | prompts: 2.4.2 1180 | transitivePeerDependencies: 1181 | - '@types/node' 1182 | - babel-plugin-macros 1183 | - supports-color 1184 | - ts-node 1185 | dev: true 1186 | 1187 | /cross-spawn@7.0.3: 1188 | resolution: {integrity: sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==} 1189 | engines: {node: '>= 8'} 1190 | dependencies: 1191 | path-key: 3.1.1 1192 | shebang-command: 2.0.0 1193 | which: 2.0.2 1194 | dev: true 1195 | 1196 | /debug@2.6.9: 1197 | resolution: {integrity: sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==} 1198 | peerDependencies: 1199 | supports-color: '*' 1200 | peerDependenciesMeta: 1201 | supports-color: 1202 | optional: true 1203 | dependencies: 1204 | ms: 2.0.0 1205 | dev: false 1206 | 1207 | /debug@4.3.4: 1208 | resolution: {integrity: sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==} 1209 | engines: {node: '>=6.0'} 1210 | peerDependencies: 1211 | supports-color: '*' 1212 | peerDependenciesMeta: 1213 | supports-color: 1214 | optional: true 1215 | dependencies: 1216 | ms: 2.1.2 1217 | dev: true 1218 | 1219 | /dedent@1.5.1: 1220 | resolution: {integrity: sha512-+LxW+KLWxu3HW3M2w2ympwtqPrqYRzU8fqi6Fhd18fBALe15blJPI/I4+UHveMVG6lJqB4JNd4UG0S5cnVHwIg==} 1221 | peerDependencies: 1222 | babel-plugin-macros: ^3.1.0 1223 | peerDependenciesMeta: 1224 | babel-plugin-macros: 1225 | optional: true 1226 | dev: true 1227 | 1228 | /deepmerge@4.3.1: 1229 | resolution: {integrity: sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==} 1230 | engines: {node: '>=0.10.0'} 1231 | dev: true 1232 | 1233 | /define-data-property@1.1.1: 1234 | resolution: {integrity: sha512-E7uGkTzkk1d0ByLeSc6ZsFS79Axg+m1P/VsgYsxHgiuc3tFSj+MjMIwe90FC4lOAZzNBdY7kkO2P2wKdsQ1vgQ==} 1235 | engines: {node: '>= 0.4'} 1236 | dependencies: 1237 | get-intrinsic: 1.2.2 1238 | gopd: 1.0.1 1239 | has-property-descriptors: 1.0.1 1240 | 1241 | /delayed-stream@1.0.0: 1242 | resolution: {integrity: sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==} 1243 | engines: {node: '>=0.4.0'} 1244 | dev: true 1245 | 1246 | /depd@2.0.0: 1247 | resolution: {integrity: sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==} 1248 | engines: {node: '>= 0.8'} 1249 | dev: false 1250 | 1251 | /destroy@1.2.0: 1252 | resolution: {integrity: sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==} 1253 | engines: {node: '>= 0.8', npm: 1.2.8000 || >= 1.4.16} 1254 | dev: false 1255 | 1256 | /detect-newline@3.1.0: 1257 | resolution: {integrity: sha512-TLz+x/vEXm/Y7P7wn1EJFNLxYpUD4TgMosxY6fAVJUnJMbupHBOncxyWUG9OpTaH9EBD7uFI5LfEgmMOc54DsA==} 1258 | engines: {node: '>=8'} 1259 | dev: true 1260 | 1261 | /dezalgo@1.0.4: 1262 | resolution: {integrity: sha512-rXSP0bf+5n0Qonsb+SVVfNfIsimO4HEtmnIpPHY8Q1UCzKlQrDMfdobr8nJOOsRgWCyMRqeSBQzmWUMq7zvVig==} 1263 | dependencies: 1264 | asap: 2.0.6 1265 | wrappy: 1.0.2 1266 | dev: true 1267 | 1268 | /diff-sequences@29.6.3: 1269 | resolution: {integrity: sha512-EjePK1srD3P08o2j4f0ExnylqRs5B9tJjcp9t1krH2qRi8CCdsYfwe9JgSLurFBWwq4uOlipzfk5fHNvwFKr8Q==} 1270 | engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} 1271 | dev: true 1272 | 1273 | /ee-first@1.1.1: 1274 | resolution: {integrity: sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==} 1275 | dev: false 1276 | 1277 | /electron-to-chromium@1.4.656: 1278 | resolution: {integrity: sha512-9AQB5eFTHyR3Gvt2t/NwR0le2jBSUNwCnMbUCejFWHD+so4tH40/dRLgoE+jxlPeWS43XJewyvCv+I8LPMl49Q==} 1279 | dev: true 1280 | 1281 | /emittery@0.13.1: 1282 | resolution: {integrity: sha512-DeWwawk6r5yR9jFgnDKYt4sLS0LmHJJi3ZOnb5/JdbYwj3nW+FxQnHIjhBKz8YLC7oRNPVM9NQ47I3CVx34eqQ==} 1283 | engines: {node: '>=12'} 1284 | dev: true 1285 | 1286 | /emoji-regex@8.0.0: 1287 | resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==} 1288 | dev: true 1289 | 1290 | /encodeurl@1.0.2: 1291 | resolution: {integrity: sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==} 1292 | engines: {node: '>= 0.8'} 1293 | dev: false 1294 | 1295 | /error-ex@1.3.2: 1296 | resolution: {integrity: sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==} 1297 | dependencies: 1298 | is-arrayish: 0.2.1 1299 | dev: true 1300 | 1301 | /escalade@3.1.1: 1302 | resolution: {integrity: sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==} 1303 | engines: {node: '>=6'} 1304 | dev: true 1305 | 1306 | /escape-html@1.0.3: 1307 | resolution: {integrity: sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==} 1308 | dev: false 1309 | 1310 | /escape-string-regexp@1.0.5: 1311 | resolution: {integrity: sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==} 1312 | engines: {node: '>=0.8.0'} 1313 | dev: true 1314 | 1315 | /escape-string-regexp@2.0.0: 1316 | resolution: {integrity: sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==} 1317 | engines: {node: '>=8'} 1318 | dev: true 1319 | 1320 | /esprima@4.0.1: 1321 | resolution: {integrity: sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==} 1322 | engines: {node: '>=4'} 1323 | hasBin: true 1324 | dev: true 1325 | 1326 | /etag@1.8.1: 1327 | resolution: {integrity: sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==} 1328 | engines: {node: '>= 0.6'} 1329 | dev: false 1330 | 1331 | /execa@5.1.1: 1332 | resolution: {integrity: sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==} 1333 | engines: {node: '>=10'} 1334 | dependencies: 1335 | cross-spawn: 7.0.3 1336 | get-stream: 6.0.1 1337 | human-signals: 2.1.0 1338 | is-stream: 2.0.1 1339 | merge-stream: 2.0.0 1340 | npm-run-path: 4.0.1 1341 | onetime: 5.1.2 1342 | signal-exit: 3.0.7 1343 | strip-final-newline: 2.0.0 1344 | dev: true 1345 | 1346 | /exit@0.1.2: 1347 | resolution: {integrity: sha512-Zk/eNKV2zbjpKzrsQ+n1G6poVbErQxJ0LBOJXaKZ1EViLzH+hrLu9cdXI4zw9dBQJslwBEpbQ2P1oS7nDxs6jQ==} 1348 | engines: {node: '>= 0.8.0'} 1349 | dev: true 1350 | 1351 | /expect@29.7.0: 1352 | resolution: {integrity: sha512-2Zks0hf1VLFYI1kbh0I5jP3KHHyCHpkfyHBzsSXRFgl/Bg9mWYfMW8oD+PdMPlEwy5HNsR9JutYy6pMeOh61nw==} 1353 | engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} 1354 | dependencies: 1355 | '@jest/expect-utils': 29.7.0 1356 | jest-get-type: 29.6.3 1357 | jest-matcher-utils: 29.7.0 1358 | jest-message-util: 29.7.0 1359 | jest-util: 29.7.0 1360 | dev: true 1361 | 1362 | /express@4.18.2: 1363 | resolution: {integrity: sha512-5/PsL6iGPdfQ/lKM1UuielYgv3BUoJfz1aUwU9vHZ+J7gyvwdQXFEBIEIaxeGf0GIcreATNyBExtalisDbuMqQ==} 1364 | engines: {node: '>= 0.10.0'} 1365 | dependencies: 1366 | accepts: 1.3.8 1367 | array-flatten: 1.1.1 1368 | body-parser: 1.20.1 1369 | content-disposition: 0.5.4 1370 | content-type: 1.0.5 1371 | cookie: 0.5.0 1372 | cookie-signature: 1.0.6 1373 | debug: 2.6.9 1374 | depd: 2.0.0 1375 | encodeurl: 1.0.2 1376 | escape-html: 1.0.3 1377 | etag: 1.8.1 1378 | finalhandler: 1.2.0 1379 | fresh: 0.5.2 1380 | http-errors: 2.0.0 1381 | merge-descriptors: 1.0.1 1382 | methods: 1.1.2 1383 | on-finished: 2.4.1 1384 | parseurl: 1.3.3 1385 | path-to-regexp: 0.1.7 1386 | proxy-addr: 2.0.7 1387 | qs: 6.11.0 1388 | range-parser: 1.2.1 1389 | safe-buffer: 5.2.1 1390 | send: 0.18.0 1391 | serve-static: 1.15.0 1392 | setprototypeof: 1.2.0 1393 | statuses: 2.0.1 1394 | type-is: 1.6.18 1395 | utils-merge: 1.0.1 1396 | vary: 1.1.2 1397 | transitivePeerDependencies: 1398 | - supports-color 1399 | dev: false 1400 | 1401 | /fast-json-stable-stringify@2.1.0: 1402 | resolution: {integrity: sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==} 1403 | dev: true 1404 | 1405 | /fast-safe-stringify@2.1.1: 1406 | resolution: {integrity: sha512-W+KJc2dmILlPplD/H4K9l9LcAHAfPtP6BY84uVLXQ6Evcz9Lcg33Y2z1IVblT6xdY54PXYVHEv+0Wpq8Io6zkA==} 1407 | dev: true 1408 | 1409 | /fb-watchman@2.0.2: 1410 | resolution: {integrity: sha512-p5161BqbuCaSnB8jIbzQHOlpgsPmK5rJVDfDKO91Axs5NC1uu3HRQm6wt9cd9/+GtQQIO53JdGXXoyDpTAsgYA==} 1411 | dependencies: 1412 | bser: 2.1.1 1413 | dev: true 1414 | 1415 | /fill-range@7.0.1: 1416 | resolution: {integrity: sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==} 1417 | engines: {node: '>=8'} 1418 | dependencies: 1419 | to-regex-range: 5.0.1 1420 | dev: true 1421 | 1422 | /finalhandler@1.2.0: 1423 | resolution: {integrity: sha512-5uXcUVftlQMFnWC9qu/svkWv3GTd2PfUhK/3PLkYNAe7FbqJMt3515HaxE6eRL74GdsriiwujiawdaB1BpEISg==} 1424 | engines: {node: '>= 0.8'} 1425 | dependencies: 1426 | debug: 2.6.9 1427 | encodeurl: 1.0.2 1428 | escape-html: 1.0.3 1429 | on-finished: 2.4.1 1430 | parseurl: 1.3.3 1431 | statuses: 2.0.1 1432 | unpipe: 1.0.0 1433 | transitivePeerDependencies: 1434 | - supports-color 1435 | dev: false 1436 | 1437 | /find-up@4.1.0: 1438 | resolution: {integrity: sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==} 1439 | engines: {node: '>=8'} 1440 | dependencies: 1441 | locate-path: 5.0.0 1442 | path-exists: 4.0.0 1443 | dev: true 1444 | 1445 | /form-data@4.0.0: 1446 | resolution: {integrity: sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==} 1447 | engines: {node: '>= 6'} 1448 | dependencies: 1449 | asynckit: 0.4.0 1450 | combined-stream: 1.0.8 1451 | mime-types: 2.1.35 1452 | dev: true 1453 | 1454 | /formidable@2.1.2: 1455 | resolution: {integrity: sha512-CM3GuJ57US06mlpQ47YcunuUZ9jpm8Vx+P2CGt2j7HpgkKZO/DJYQ0Bobim8G6PFQmK5lOqOOdUXboU+h73A4g==} 1456 | dependencies: 1457 | dezalgo: 1.0.4 1458 | hexoid: 1.0.0 1459 | once: 1.4.0 1460 | qs: 6.11.2 1461 | dev: true 1462 | 1463 | /forwarded@0.2.0: 1464 | resolution: {integrity: sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==} 1465 | engines: {node: '>= 0.6'} 1466 | dev: false 1467 | 1468 | /fresh@0.5.2: 1469 | resolution: {integrity: sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==} 1470 | engines: {node: '>= 0.6'} 1471 | dev: false 1472 | 1473 | /fs.realpath@1.0.0: 1474 | resolution: {integrity: sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==} 1475 | dev: true 1476 | 1477 | /fsevents@2.3.3: 1478 | resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==} 1479 | engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} 1480 | os: [darwin] 1481 | requiresBuild: true 1482 | dev: true 1483 | optional: true 1484 | 1485 | /function-bind@1.1.2: 1486 | resolution: {integrity: sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==} 1487 | 1488 | /gensync@1.0.0-beta.2: 1489 | resolution: {integrity: sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==} 1490 | engines: {node: '>=6.9.0'} 1491 | dev: true 1492 | 1493 | /get-caller-file@2.0.5: 1494 | resolution: {integrity: sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==} 1495 | engines: {node: 6.* || 8.* || >= 10.*} 1496 | dev: true 1497 | 1498 | /get-intrinsic@1.2.2: 1499 | resolution: {integrity: sha512-0gSo4ml/0j98Y3lngkFEot/zhiCeWsbYIlZ+uZOVgzLyLaUw7wxUL+nCTP0XJvJg1AXulJRI3UJi8GsbDuxdGA==} 1500 | dependencies: 1501 | function-bind: 1.1.2 1502 | has-proto: 1.0.1 1503 | has-symbols: 1.0.3 1504 | hasown: 2.0.0 1505 | 1506 | /get-package-type@0.1.0: 1507 | resolution: {integrity: sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q==} 1508 | engines: {node: '>=8.0.0'} 1509 | dev: true 1510 | 1511 | /get-stream@6.0.1: 1512 | resolution: {integrity: sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==} 1513 | engines: {node: '>=10'} 1514 | dev: true 1515 | 1516 | /glob@7.2.3: 1517 | resolution: {integrity: sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==} 1518 | dependencies: 1519 | fs.realpath: 1.0.0 1520 | inflight: 1.0.6 1521 | inherits: 2.0.4 1522 | minimatch: 3.1.2 1523 | once: 1.4.0 1524 | path-is-absolute: 1.0.1 1525 | dev: true 1526 | 1527 | /globals@11.12.0: 1528 | resolution: {integrity: sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==} 1529 | engines: {node: '>=4'} 1530 | dev: true 1531 | 1532 | /gopd@1.0.1: 1533 | resolution: {integrity: sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==} 1534 | dependencies: 1535 | get-intrinsic: 1.2.2 1536 | 1537 | /graceful-fs@4.2.11: 1538 | resolution: {integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==} 1539 | dev: true 1540 | 1541 | /has-flag@3.0.0: 1542 | resolution: {integrity: sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==} 1543 | engines: {node: '>=4'} 1544 | dev: true 1545 | 1546 | /has-flag@4.0.0: 1547 | resolution: {integrity: sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==} 1548 | engines: {node: '>=8'} 1549 | dev: true 1550 | 1551 | /has-property-descriptors@1.0.1: 1552 | resolution: {integrity: sha512-VsX8eaIewvas0xnvinAe9bw4WfIeODpGYikiWYLH+dma0Jw6KHYqWiWfhQlgOVK8D6PvjubK5Uc4P0iIhIcNVg==} 1553 | dependencies: 1554 | get-intrinsic: 1.2.2 1555 | 1556 | /has-proto@1.0.1: 1557 | resolution: {integrity: sha512-7qE+iP+O+bgF9clE5+UoBFzE65mlBiVj3tKCrlNQ0Ogwm0BjpT/gK4SlLYDMybDh5I3TCTKnPPa0oMG7JDYrhg==} 1558 | engines: {node: '>= 0.4'} 1559 | 1560 | /has-symbols@1.0.3: 1561 | resolution: {integrity: sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==} 1562 | engines: {node: '>= 0.4'} 1563 | 1564 | /hasown@2.0.0: 1565 | resolution: {integrity: sha512-vUptKVTpIJhcczKBbgnS+RtcuYMB8+oNzPK2/Hp3hanz8JmpATdmmgLgSaadVREkDm+e2giHwY3ZRkyjSIDDFA==} 1566 | engines: {node: '>= 0.4'} 1567 | dependencies: 1568 | function-bind: 1.1.2 1569 | 1570 | /hexoid@1.0.0: 1571 | resolution: {integrity: sha512-QFLV0taWQOZtvIRIAdBChesmogZrtuXvVWsFHZTk2SU+anspqZ2vMnoLg7IE1+Uk16N19APic1BuF8bC8c2m5g==} 1572 | engines: {node: '>=8'} 1573 | dev: true 1574 | 1575 | /html-escaper@2.0.2: 1576 | resolution: {integrity: sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==} 1577 | dev: true 1578 | 1579 | /http-errors@2.0.0: 1580 | resolution: {integrity: sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==} 1581 | engines: {node: '>= 0.8'} 1582 | dependencies: 1583 | depd: 2.0.0 1584 | inherits: 2.0.4 1585 | setprototypeof: 1.2.0 1586 | statuses: 2.0.1 1587 | toidentifier: 1.0.1 1588 | dev: false 1589 | 1590 | /human-signals@2.1.0: 1591 | resolution: {integrity: sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==} 1592 | engines: {node: '>=10.17.0'} 1593 | dev: true 1594 | 1595 | /iconv-lite@0.4.24: 1596 | resolution: {integrity: sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==} 1597 | engines: {node: '>=0.10.0'} 1598 | dependencies: 1599 | safer-buffer: 2.1.2 1600 | dev: false 1601 | 1602 | /import-local@3.1.0: 1603 | resolution: {integrity: sha512-ASB07uLtnDs1o6EHjKpX34BKYDSqnFerfTOJL2HvMqF70LnxpjkzDB8J44oT9pu4AMPkQwf8jl6szgvNd2tRIg==} 1604 | engines: {node: '>=8'} 1605 | hasBin: true 1606 | dependencies: 1607 | pkg-dir: 4.2.0 1608 | resolve-cwd: 3.0.0 1609 | dev: true 1610 | 1611 | /imurmurhash@0.1.4: 1612 | resolution: {integrity: sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==} 1613 | engines: {node: '>=0.8.19'} 1614 | dev: true 1615 | 1616 | /inflight@1.0.6: 1617 | resolution: {integrity: sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==} 1618 | dependencies: 1619 | once: 1.4.0 1620 | wrappy: 1.0.2 1621 | dev: true 1622 | 1623 | /inherits@2.0.4: 1624 | resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==} 1625 | 1626 | /ipaddr.js@1.9.1: 1627 | resolution: {integrity: sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==} 1628 | engines: {node: '>= 0.10'} 1629 | dev: false 1630 | 1631 | /is-arrayish@0.2.1: 1632 | resolution: {integrity: sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==} 1633 | dev: true 1634 | 1635 | /is-core-module@2.13.1: 1636 | resolution: {integrity: sha512-hHrIjvZsftOsvKSn2TRYl63zvxsgE0K+0mYMoH6gD4omR5IWB2KynivBQczo3+wF1cCkjzvptnI9Q0sPU66ilw==} 1637 | dependencies: 1638 | hasown: 2.0.0 1639 | dev: true 1640 | 1641 | /is-fullwidth-code-point@3.0.0: 1642 | resolution: {integrity: sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==} 1643 | engines: {node: '>=8'} 1644 | dev: true 1645 | 1646 | /is-generator-fn@2.1.0: 1647 | resolution: {integrity: sha512-cTIB4yPYL/Grw0EaSzASzg6bBy9gqCofvWN8okThAYIxKJZC+udlRAmGbM0XLeniEJSs8uEgHPGuHSe1XsOLSQ==} 1648 | engines: {node: '>=6'} 1649 | dev: true 1650 | 1651 | /is-number@7.0.0: 1652 | resolution: {integrity: sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==} 1653 | engines: {node: '>=0.12.0'} 1654 | dev: true 1655 | 1656 | /is-stream@2.0.1: 1657 | resolution: {integrity: sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==} 1658 | engines: {node: '>=8'} 1659 | dev: true 1660 | 1661 | /isexe@2.0.0: 1662 | resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==} 1663 | dev: true 1664 | 1665 | /istanbul-lib-coverage@3.2.2: 1666 | resolution: {integrity: sha512-O8dpsF+r0WV/8MNRKfnmrtCWhuKjxrq2w+jpzBL5UZKTi2LeVWnWOmWRxFlesJONmc+wLAGvKQZEOanko0LFTg==} 1667 | engines: {node: '>=8'} 1668 | dev: true 1669 | 1670 | /istanbul-lib-instrument@5.2.1: 1671 | resolution: {integrity: sha512-pzqtp31nLv/XFOzXGuvhCb8qhjmTVo5vjVk19XE4CRlSWz0KoeJ3bw9XsA7nOp9YBf4qHjwBxkDzKcME/J29Yg==} 1672 | engines: {node: '>=8'} 1673 | dependencies: 1674 | '@babel/core': 7.23.9 1675 | '@babel/parser': 7.23.9 1676 | '@istanbuljs/schema': 0.1.3 1677 | istanbul-lib-coverage: 3.2.2 1678 | semver: 6.3.1 1679 | transitivePeerDependencies: 1680 | - supports-color 1681 | dev: true 1682 | 1683 | /istanbul-lib-instrument@6.0.1: 1684 | resolution: {integrity: sha512-EAMEJBsYuyyztxMxW3g7ugGPkrZsV57v0Hmv3mm1uQsmB+QnZuepg731CRaIgeUVSdmsTngOkSnauNF8p7FIhA==} 1685 | engines: {node: '>=10'} 1686 | dependencies: 1687 | '@babel/core': 7.23.9 1688 | '@babel/parser': 7.23.9 1689 | '@istanbuljs/schema': 0.1.3 1690 | istanbul-lib-coverage: 3.2.2 1691 | semver: 7.5.4 1692 | transitivePeerDependencies: 1693 | - supports-color 1694 | dev: true 1695 | 1696 | /istanbul-lib-report@3.0.1: 1697 | resolution: {integrity: sha512-GCfE1mtsHGOELCU8e/Z7YWzpmybrx/+dSTfLrvY8qRmaY6zXTKWn6WQIjaAFw069icm6GVMNkgu0NzI4iPZUNw==} 1698 | engines: {node: '>=10'} 1699 | dependencies: 1700 | istanbul-lib-coverage: 3.2.2 1701 | make-dir: 4.0.0 1702 | supports-color: 7.2.0 1703 | dev: true 1704 | 1705 | /istanbul-lib-source-maps@4.0.1: 1706 | resolution: {integrity: sha512-n3s8EwkdFIJCG3BPKBYvskgXGoy88ARzvegkitk60NxRdwltLOTaH7CUiMRXvwYorl0Q712iEjcWB+fK/MrWVw==} 1707 | engines: {node: '>=10'} 1708 | dependencies: 1709 | debug: 4.3.4 1710 | istanbul-lib-coverage: 3.2.2 1711 | source-map: 0.6.1 1712 | transitivePeerDependencies: 1713 | - supports-color 1714 | dev: true 1715 | 1716 | /istanbul-reports@3.1.6: 1717 | resolution: {integrity: sha512-TLgnMkKg3iTDsQ9PbPTdpfAK2DzjF9mqUG7RMgcQl8oFjad8ob4laGxv5XV5U9MAfx8D6tSJiUyuAwzLicaxlg==} 1718 | engines: {node: '>=8'} 1719 | dependencies: 1720 | html-escaper: 2.0.2 1721 | istanbul-lib-report: 3.0.1 1722 | dev: true 1723 | 1724 | /jest-changed-files@29.7.0: 1725 | resolution: {integrity: sha512-fEArFiwf1BpQ+4bXSprcDc3/x4HSzL4al2tozwVpDFpsxALjLYdyiIK4e5Vz66GQJIbXJ82+35PtysofptNX2w==} 1726 | engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} 1727 | dependencies: 1728 | execa: 5.1.1 1729 | jest-util: 29.7.0 1730 | p-limit: 3.1.0 1731 | dev: true 1732 | 1733 | /jest-circus@29.7.0: 1734 | resolution: {integrity: sha512-3E1nCMgipcTkCocFwM90XXQab9bS+GMsjdpmPrlelaxwD93Ad8iVEjX/vvHPdLPnFf+L40u+5+iutRdA1N9myw==} 1735 | engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} 1736 | dependencies: 1737 | '@jest/environment': 29.7.0 1738 | '@jest/expect': 29.7.0 1739 | '@jest/test-result': 29.7.0 1740 | '@jest/types': 29.6.3 1741 | '@types/node': 20.11.16 1742 | chalk: 4.1.2 1743 | co: 4.6.0 1744 | dedent: 1.5.1 1745 | is-generator-fn: 2.1.0 1746 | jest-each: 29.7.0 1747 | jest-matcher-utils: 29.7.0 1748 | jest-message-util: 29.7.0 1749 | jest-runtime: 29.7.0 1750 | jest-snapshot: 29.7.0 1751 | jest-util: 29.7.0 1752 | p-limit: 3.1.0 1753 | pretty-format: 29.7.0 1754 | pure-rand: 6.0.4 1755 | slash: 3.0.0 1756 | stack-utils: 2.0.6 1757 | transitivePeerDependencies: 1758 | - babel-plugin-macros 1759 | - supports-color 1760 | dev: true 1761 | 1762 | /jest-cli@29.7.0: 1763 | resolution: {integrity: sha512-OVVobw2IubN/GSYsxETi+gOe7Ka59EFMR/twOU3Jb2GnKKeMGJB5SGUUrEz3SFVmJASUdZUzy83sLNNQ2gZslg==} 1764 | engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} 1765 | hasBin: true 1766 | peerDependencies: 1767 | node-notifier: ^8.0.1 || ^9.0.0 || ^10.0.0 1768 | peerDependenciesMeta: 1769 | node-notifier: 1770 | optional: true 1771 | dependencies: 1772 | '@jest/core': 29.7.0 1773 | '@jest/test-result': 29.7.0 1774 | '@jest/types': 29.6.3 1775 | chalk: 4.1.2 1776 | create-jest: 29.7.0 1777 | exit: 0.1.2 1778 | import-local: 3.1.0 1779 | jest-config: 29.7.0(@types/node@20.11.16) 1780 | jest-util: 29.7.0 1781 | jest-validate: 29.7.0 1782 | yargs: 17.7.2 1783 | transitivePeerDependencies: 1784 | - '@types/node' 1785 | - babel-plugin-macros 1786 | - supports-color 1787 | - ts-node 1788 | dev: true 1789 | 1790 | /jest-config@29.7.0(@types/node@20.11.16): 1791 | resolution: {integrity: sha512-uXbpfeQ7R6TZBqI3/TxCU4q4ttk3u0PJeC+E0zbfSoSjq6bJ7buBPxzQPL0ifrkY4DNu4JUdk0ImlBUYi840eQ==} 1792 | engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} 1793 | peerDependencies: 1794 | '@types/node': '*' 1795 | ts-node: '>=9.0.0' 1796 | peerDependenciesMeta: 1797 | '@types/node': 1798 | optional: true 1799 | ts-node: 1800 | optional: true 1801 | dependencies: 1802 | '@babel/core': 7.23.9 1803 | '@jest/test-sequencer': 29.7.0 1804 | '@jest/types': 29.6.3 1805 | '@types/node': 20.11.16 1806 | babel-jest: 29.7.0(@babel/core@7.23.9) 1807 | chalk: 4.1.2 1808 | ci-info: 3.9.0 1809 | deepmerge: 4.3.1 1810 | glob: 7.2.3 1811 | graceful-fs: 4.2.11 1812 | jest-circus: 29.7.0 1813 | jest-environment-node: 29.7.0 1814 | jest-get-type: 29.6.3 1815 | jest-regex-util: 29.6.3 1816 | jest-resolve: 29.7.0 1817 | jest-runner: 29.7.0 1818 | jest-util: 29.7.0 1819 | jest-validate: 29.7.0 1820 | micromatch: 4.0.5 1821 | parse-json: 5.2.0 1822 | pretty-format: 29.7.0 1823 | slash: 3.0.0 1824 | strip-json-comments: 3.1.1 1825 | transitivePeerDependencies: 1826 | - babel-plugin-macros 1827 | - supports-color 1828 | dev: true 1829 | 1830 | /jest-diff@29.7.0: 1831 | resolution: {integrity: sha512-LMIgiIrhigmPrs03JHpxUh2yISK3vLFPkAodPeo0+BuF7wA2FoQbkEg1u8gBYBThncu7e1oEDUfIXVuTqLRUjw==} 1832 | engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} 1833 | dependencies: 1834 | chalk: 4.1.2 1835 | diff-sequences: 29.6.3 1836 | jest-get-type: 29.6.3 1837 | pretty-format: 29.7.0 1838 | dev: true 1839 | 1840 | /jest-docblock@29.7.0: 1841 | resolution: {integrity: sha512-q617Auw3A612guyaFgsbFeYpNP5t2aoUNLwBUbc/0kD1R4t9ixDbyFTHd1nok4epoVFpr7PmeWHrhvuV3XaJ4g==} 1842 | engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} 1843 | dependencies: 1844 | detect-newline: 3.1.0 1845 | dev: true 1846 | 1847 | /jest-each@29.7.0: 1848 | resolution: {integrity: sha512-gns+Er14+ZrEoC5fhOfYCY1LOHHr0TI+rQUHZS8Ttw2l7gl+80eHc/gFf2Ktkw0+SIACDTeWvpFcv3B04VembQ==} 1849 | engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} 1850 | dependencies: 1851 | '@jest/types': 29.6.3 1852 | chalk: 4.1.2 1853 | jest-get-type: 29.6.3 1854 | jest-util: 29.7.0 1855 | pretty-format: 29.7.0 1856 | dev: true 1857 | 1858 | /jest-environment-node@29.7.0: 1859 | resolution: {integrity: sha512-DOSwCRqXirTOyheM+4d5YZOrWcdu0LNZ87ewUoywbcb2XR4wKgqiG8vNeYwhjFMbEkfju7wx2GYH0P2gevGvFw==} 1860 | engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} 1861 | dependencies: 1862 | '@jest/environment': 29.7.0 1863 | '@jest/fake-timers': 29.7.0 1864 | '@jest/types': 29.6.3 1865 | '@types/node': 20.11.16 1866 | jest-mock: 29.7.0 1867 | jest-util: 29.7.0 1868 | dev: true 1869 | 1870 | /jest-get-type@29.6.3: 1871 | resolution: {integrity: sha512-zrteXnqYxfQh7l5FHyL38jL39di8H8rHoecLH3JNxH3BwOrBsNeabdap5e0I23lD4HHI8W5VFBZqG4Eaq5LNcw==} 1872 | engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} 1873 | dev: true 1874 | 1875 | /jest-haste-map@29.7.0: 1876 | resolution: {integrity: sha512-fP8u2pyfqx0K1rGn1R9pyE0/KTn+G7PxktWidOBTqFPLYX0b9ksaMFkhK5vrS3DVun09pckLdlx90QthlW7AmA==} 1877 | engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} 1878 | dependencies: 1879 | '@jest/types': 29.6.3 1880 | '@types/graceful-fs': 4.1.9 1881 | '@types/node': 20.11.16 1882 | anymatch: 3.1.3 1883 | fb-watchman: 2.0.2 1884 | graceful-fs: 4.2.11 1885 | jest-regex-util: 29.6.3 1886 | jest-util: 29.7.0 1887 | jest-worker: 29.7.0 1888 | micromatch: 4.0.5 1889 | walker: 1.0.8 1890 | optionalDependencies: 1891 | fsevents: 2.3.3 1892 | dev: true 1893 | 1894 | /jest-leak-detector@29.7.0: 1895 | resolution: {integrity: sha512-kYA8IJcSYtST2BY9I+SMC32nDpBT3J2NvWJx8+JCuCdl/CR1I4EKUJROiP8XtCcxqgTTBGJNdbB1A8XRKbTetw==} 1896 | engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} 1897 | dependencies: 1898 | jest-get-type: 29.6.3 1899 | pretty-format: 29.7.0 1900 | dev: true 1901 | 1902 | /jest-matcher-utils@29.7.0: 1903 | resolution: {integrity: sha512-sBkD+Xi9DtcChsI3L3u0+N0opgPYnCRPtGcQYrgXmR+hmt/fYfWAL0xRXYU8eWOdfuLgBe0YCW3AFtnRLagq/g==} 1904 | engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} 1905 | dependencies: 1906 | chalk: 4.1.2 1907 | jest-diff: 29.7.0 1908 | jest-get-type: 29.6.3 1909 | pretty-format: 29.7.0 1910 | dev: true 1911 | 1912 | /jest-message-util@29.7.0: 1913 | resolution: {integrity: sha512-GBEV4GRADeP+qtB2+6u61stea8mGcOT4mCtrYISZwfu9/ISHFJ/5zOMXYbpBE9RsS5+Gb63DW4FgmnKJ79Kf6w==} 1914 | engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} 1915 | dependencies: 1916 | '@babel/code-frame': 7.23.5 1917 | '@jest/types': 29.6.3 1918 | '@types/stack-utils': 2.0.3 1919 | chalk: 4.1.2 1920 | graceful-fs: 4.2.11 1921 | micromatch: 4.0.5 1922 | pretty-format: 29.7.0 1923 | slash: 3.0.0 1924 | stack-utils: 2.0.6 1925 | dev: true 1926 | 1927 | /jest-mock@29.7.0: 1928 | resolution: {integrity: sha512-ITOMZn+UkYS4ZFh83xYAOzWStloNzJFO2s8DWrE4lhtGD+AorgnbkiKERe4wQVBydIGPx059g6riW5Btp6Llnw==} 1929 | engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} 1930 | dependencies: 1931 | '@jest/types': 29.6.3 1932 | '@types/node': 20.11.16 1933 | jest-util: 29.7.0 1934 | dev: true 1935 | 1936 | /jest-pnp-resolver@1.2.3(jest-resolve@29.7.0): 1937 | resolution: {integrity: sha512-+3NpwQEnRoIBtx4fyhblQDPgJI0H1IEIkX7ShLUjPGA7TtUTvI1oiKi3SR4oBR0hQhQR80l4WAe5RrXBwWMA8w==} 1938 | engines: {node: '>=6'} 1939 | peerDependencies: 1940 | jest-resolve: '*' 1941 | peerDependenciesMeta: 1942 | jest-resolve: 1943 | optional: true 1944 | dependencies: 1945 | jest-resolve: 29.7.0 1946 | dev: true 1947 | 1948 | /jest-regex-util@29.6.3: 1949 | resolution: {integrity: sha512-KJJBsRCyyLNWCNBOvZyRDnAIfUiRJ8v+hOBQYGn8gDyF3UegwiP4gwRR3/SDa42g1YbVycTidUF3rKjyLFDWbg==} 1950 | engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} 1951 | dev: true 1952 | 1953 | /jest-resolve-dependencies@29.7.0: 1954 | resolution: {integrity: sha512-un0zD/6qxJ+S0et7WxeI3H5XSe9lTBBR7bOHCHXkKR6luG5mwDDlIzVQ0V5cZCuoTgEdcdwzTghYkTWfubi+nA==} 1955 | engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} 1956 | dependencies: 1957 | jest-regex-util: 29.6.3 1958 | jest-snapshot: 29.7.0 1959 | transitivePeerDependencies: 1960 | - supports-color 1961 | dev: true 1962 | 1963 | /jest-resolve@29.7.0: 1964 | resolution: {integrity: sha512-IOVhZSrg+UvVAshDSDtHyFCCBUl/Q3AAJv8iZ6ZjnZ74xzvwuzLXid9IIIPgTnY62SJjfuupMKZsZQRsCvxEgA==} 1965 | engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} 1966 | dependencies: 1967 | chalk: 4.1.2 1968 | graceful-fs: 4.2.11 1969 | jest-haste-map: 29.7.0 1970 | jest-pnp-resolver: 1.2.3(jest-resolve@29.7.0) 1971 | jest-util: 29.7.0 1972 | jest-validate: 29.7.0 1973 | resolve: 1.22.8 1974 | resolve.exports: 2.0.2 1975 | slash: 3.0.0 1976 | dev: true 1977 | 1978 | /jest-runner@29.7.0: 1979 | resolution: {integrity: sha512-fsc4N6cPCAahybGBfTRcq5wFR6fpLznMg47sY5aDpsoejOcVYFb07AHuSnR0liMcPTgBsA3ZJL6kFOjPdoNipQ==} 1980 | engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} 1981 | dependencies: 1982 | '@jest/console': 29.7.0 1983 | '@jest/environment': 29.7.0 1984 | '@jest/test-result': 29.7.0 1985 | '@jest/transform': 29.7.0 1986 | '@jest/types': 29.6.3 1987 | '@types/node': 20.11.16 1988 | chalk: 4.1.2 1989 | emittery: 0.13.1 1990 | graceful-fs: 4.2.11 1991 | jest-docblock: 29.7.0 1992 | jest-environment-node: 29.7.0 1993 | jest-haste-map: 29.7.0 1994 | jest-leak-detector: 29.7.0 1995 | jest-message-util: 29.7.0 1996 | jest-resolve: 29.7.0 1997 | jest-runtime: 29.7.0 1998 | jest-util: 29.7.0 1999 | jest-watcher: 29.7.0 2000 | jest-worker: 29.7.0 2001 | p-limit: 3.1.0 2002 | source-map-support: 0.5.13 2003 | transitivePeerDependencies: 2004 | - supports-color 2005 | dev: true 2006 | 2007 | /jest-runtime@29.7.0: 2008 | resolution: {integrity: sha512-gUnLjgwdGqW7B4LvOIkbKs9WGbn+QLqRQQ9juC6HndeDiezIwhDP+mhMwHWCEcfQ5RUXa6OPnFF8BJh5xegwwQ==} 2009 | engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} 2010 | dependencies: 2011 | '@jest/environment': 29.7.0 2012 | '@jest/fake-timers': 29.7.0 2013 | '@jest/globals': 29.7.0 2014 | '@jest/source-map': 29.6.3 2015 | '@jest/test-result': 29.7.0 2016 | '@jest/transform': 29.7.0 2017 | '@jest/types': 29.6.3 2018 | '@types/node': 20.11.16 2019 | chalk: 4.1.2 2020 | cjs-module-lexer: 1.2.3 2021 | collect-v8-coverage: 1.0.2 2022 | glob: 7.2.3 2023 | graceful-fs: 4.2.11 2024 | jest-haste-map: 29.7.0 2025 | jest-message-util: 29.7.0 2026 | jest-mock: 29.7.0 2027 | jest-regex-util: 29.6.3 2028 | jest-resolve: 29.7.0 2029 | jest-snapshot: 29.7.0 2030 | jest-util: 29.7.0 2031 | slash: 3.0.0 2032 | strip-bom: 4.0.0 2033 | transitivePeerDependencies: 2034 | - supports-color 2035 | dev: true 2036 | 2037 | /jest-snapshot@29.7.0: 2038 | resolution: {integrity: sha512-Rm0BMWtxBcioHr1/OX5YCP8Uov4riHvKPknOGs804Zg9JGZgmIBkbtlxJC/7Z4msKYVbIJtfU+tKb8xlYNfdkw==} 2039 | engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} 2040 | dependencies: 2041 | '@babel/core': 7.23.9 2042 | '@babel/generator': 7.23.6 2043 | '@babel/plugin-syntax-jsx': 7.23.3(@babel/core@7.23.9) 2044 | '@babel/plugin-syntax-typescript': 7.23.3(@babel/core@7.23.9) 2045 | '@babel/types': 7.23.9 2046 | '@jest/expect-utils': 29.7.0 2047 | '@jest/transform': 29.7.0 2048 | '@jest/types': 29.6.3 2049 | babel-preset-current-node-syntax: 1.0.1(@babel/core@7.23.9) 2050 | chalk: 4.1.2 2051 | expect: 29.7.0 2052 | graceful-fs: 4.2.11 2053 | jest-diff: 29.7.0 2054 | jest-get-type: 29.6.3 2055 | jest-matcher-utils: 29.7.0 2056 | jest-message-util: 29.7.0 2057 | jest-util: 29.7.0 2058 | natural-compare: 1.4.0 2059 | pretty-format: 29.7.0 2060 | semver: 7.5.4 2061 | transitivePeerDependencies: 2062 | - supports-color 2063 | dev: true 2064 | 2065 | /jest-util@29.7.0: 2066 | resolution: {integrity: sha512-z6EbKajIpqGKU56y5KBUgy1dt1ihhQJgWzUlZHArA/+X2ad7Cb5iF+AK1EWVL/Bo7Rz9uurpqw6SiBCefUbCGA==} 2067 | engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} 2068 | dependencies: 2069 | '@jest/types': 29.6.3 2070 | '@types/node': 20.11.16 2071 | chalk: 4.1.2 2072 | ci-info: 3.9.0 2073 | graceful-fs: 4.2.11 2074 | picomatch: 2.3.1 2075 | dev: true 2076 | 2077 | /jest-validate@29.7.0: 2078 | resolution: {integrity: sha512-ZB7wHqaRGVw/9hST/OuFUReG7M8vKeq0/J2egIGLdvjHCmYqGARhzXmtgi+gVeZ5uXFF219aOc3Ls2yLg27tkw==} 2079 | engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} 2080 | dependencies: 2081 | '@jest/types': 29.6.3 2082 | camelcase: 6.3.0 2083 | chalk: 4.1.2 2084 | jest-get-type: 29.6.3 2085 | leven: 3.1.0 2086 | pretty-format: 29.7.0 2087 | dev: true 2088 | 2089 | /jest-watcher@29.7.0: 2090 | resolution: {integrity: sha512-49Fg7WXkU3Vl2h6LbLtMQ/HyB6rXSIX7SqvBLQmssRBGN9I0PNvPmAmCWSOY6SOvrjhI/F7/bGAv9RtnsPA03g==} 2091 | engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} 2092 | dependencies: 2093 | '@jest/test-result': 29.7.0 2094 | '@jest/types': 29.6.3 2095 | '@types/node': 20.11.16 2096 | ansi-escapes: 4.3.2 2097 | chalk: 4.1.2 2098 | emittery: 0.13.1 2099 | jest-util: 29.7.0 2100 | string-length: 4.0.2 2101 | dev: true 2102 | 2103 | /jest-worker@29.7.0: 2104 | resolution: {integrity: sha512-eIz2msL/EzL9UFTFFx7jBTkeZfku0yUAyZZZmJ93H2TYEiroIx2PQjEXcwYtYl8zXCxb+PAmA2hLIt/6ZEkPHw==} 2105 | engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} 2106 | dependencies: 2107 | '@types/node': 20.11.16 2108 | jest-util: 29.7.0 2109 | merge-stream: 2.0.0 2110 | supports-color: 8.1.1 2111 | dev: true 2112 | 2113 | /jest@29.7.0: 2114 | resolution: {integrity: sha512-NIy3oAFp9shda19hy4HK0HRTWKtPJmGdnvywu01nOqNC2vZg+Z+fvJDxpMQA88eb2I9EcafcdjYgsDthnYTvGw==} 2115 | engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} 2116 | hasBin: true 2117 | peerDependencies: 2118 | node-notifier: ^8.0.1 || ^9.0.0 || ^10.0.0 2119 | peerDependenciesMeta: 2120 | node-notifier: 2121 | optional: true 2122 | dependencies: 2123 | '@jest/core': 29.7.0 2124 | '@jest/types': 29.6.3 2125 | import-local: 3.1.0 2126 | jest-cli: 29.7.0 2127 | transitivePeerDependencies: 2128 | - '@types/node' 2129 | - babel-plugin-macros 2130 | - supports-color 2131 | - ts-node 2132 | dev: true 2133 | 2134 | /js-tokens@4.0.0: 2135 | resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==} 2136 | dev: true 2137 | 2138 | /js-yaml@3.14.1: 2139 | resolution: {integrity: sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==} 2140 | hasBin: true 2141 | dependencies: 2142 | argparse: 1.0.10 2143 | esprima: 4.0.1 2144 | dev: true 2145 | 2146 | /jsesc@2.5.2: 2147 | resolution: {integrity: sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==} 2148 | engines: {node: '>=4'} 2149 | hasBin: true 2150 | dev: true 2151 | 2152 | /json-parse-even-better-errors@2.3.1: 2153 | resolution: {integrity: sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==} 2154 | dev: true 2155 | 2156 | /json5@2.2.3: 2157 | resolution: {integrity: sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==} 2158 | engines: {node: '>=6'} 2159 | hasBin: true 2160 | dev: true 2161 | 2162 | /kleur@3.0.3: 2163 | resolution: {integrity: sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==} 2164 | engines: {node: '>=6'} 2165 | dev: true 2166 | 2167 | /leven@3.1.0: 2168 | resolution: {integrity: sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A==} 2169 | engines: {node: '>=6'} 2170 | dev: true 2171 | 2172 | /lines-and-columns@1.2.4: 2173 | resolution: {integrity: sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==} 2174 | dev: true 2175 | 2176 | /locate-path@5.0.0: 2177 | resolution: {integrity: sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==} 2178 | engines: {node: '>=8'} 2179 | dependencies: 2180 | p-locate: 4.1.0 2181 | dev: true 2182 | 2183 | /lodash.memoize@4.1.2: 2184 | resolution: {integrity: sha512-t7j+NzmgnQzTAYXcsHYLgimltOV1MXHtlOWf6GjL9Kj8GK5FInw5JotxvbOs+IvV1/Dzo04/fCGfLVs7aXb4Ag==} 2185 | dev: true 2186 | 2187 | /lru-cache@5.1.1: 2188 | resolution: {integrity: sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==} 2189 | dependencies: 2190 | yallist: 3.1.1 2191 | dev: true 2192 | 2193 | /lru-cache@6.0.0: 2194 | resolution: {integrity: sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==} 2195 | engines: {node: '>=10'} 2196 | dependencies: 2197 | yallist: 4.0.0 2198 | dev: true 2199 | 2200 | /make-dir@4.0.0: 2201 | resolution: {integrity: sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw==} 2202 | engines: {node: '>=10'} 2203 | dependencies: 2204 | semver: 7.5.4 2205 | dev: true 2206 | 2207 | /make-error@1.3.6: 2208 | resolution: {integrity: sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==} 2209 | dev: true 2210 | 2211 | /makeerror@1.0.12: 2212 | resolution: {integrity: sha512-JmqCvUhmt43madlpFzG4BQzG2Z3m6tvQDNKdClZnO3VbIudJYmxsT0FNJMeiB2+JTSlTQTSbU8QdesVmwJcmLg==} 2213 | dependencies: 2214 | tmpl: 1.0.5 2215 | dev: true 2216 | 2217 | /media-typer@0.3.0: 2218 | resolution: {integrity: sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==} 2219 | engines: {node: '>= 0.6'} 2220 | dev: false 2221 | 2222 | /merge-descriptors@1.0.1: 2223 | resolution: {integrity: sha512-cCi6g3/Zr1iqQi6ySbseM1Xvooa98N0w31jzUYrXPX2xqObmFGHJ0tQ5u74H3mVh7wLouTseZyYIq39g8cNp1w==} 2224 | dev: false 2225 | 2226 | /merge-stream@2.0.0: 2227 | resolution: {integrity: sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==} 2228 | dev: true 2229 | 2230 | /methods@1.1.2: 2231 | resolution: {integrity: sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==} 2232 | engines: {node: '>= 0.6'} 2233 | 2234 | /micromatch@4.0.5: 2235 | resolution: {integrity: sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==} 2236 | engines: {node: '>=8.6'} 2237 | dependencies: 2238 | braces: 3.0.2 2239 | picomatch: 2.3.1 2240 | dev: true 2241 | 2242 | /mime-db@1.52.0: 2243 | resolution: {integrity: sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==} 2244 | engines: {node: '>= 0.6'} 2245 | 2246 | /mime-types@2.1.35: 2247 | resolution: {integrity: sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==} 2248 | engines: {node: '>= 0.6'} 2249 | dependencies: 2250 | mime-db: 1.52.0 2251 | 2252 | /mime@1.6.0: 2253 | resolution: {integrity: sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==} 2254 | engines: {node: '>=4'} 2255 | hasBin: true 2256 | dev: false 2257 | 2258 | /mime@2.6.0: 2259 | resolution: {integrity: sha512-USPkMeET31rOMiarsBNIHZKLGgvKc/LrjofAnBlOttf5ajRvqiRA8QsenbcooctK6d6Ts6aqZXBA+XbkKthiQg==} 2260 | engines: {node: '>=4.0.0'} 2261 | hasBin: true 2262 | dev: true 2263 | 2264 | /mimic-fn@2.1.0: 2265 | resolution: {integrity: sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==} 2266 | engines: {node: '>=6'} 2267 | dev: true 2268 | 2269 | /minimatch@3.1.2: 2270 | resolution: {integrity: sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==} 2271 | dependencies: 2272 | brace-expansion: 1.1.11 2273 | dev: true 2274 | 2275 | /ms@2.0.0: 2276 | resolution: {integrity: sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==} 2277 | dev: false 2278 | 2279 | /ms@2.1.2: 2280 | resolution: {integrity: sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==} 2281 | dev: true 2282 | 2283 | /ms@2.1.3: 2284 | resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==} 2285 | dev: false 2286 | 2287 | /natural-compare@1.4.0: 2288 | resolution: {integrity: sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==} 2289 | dev: true 2290 | 2291 | /negotiator@0.6.3: 2292 | resolution: {integrity: sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==} 2293 | engines: {node: '>= 0.6'} 2294 | dev: false 2295 | 2296 | /node-int64@0.4.0: 2297 | resolution: {integrity: sha512-O5lz91xSOeoXP6DulyHfllpq+Eg00MWitZIbtPfoSEvqIHdl5gfcY6hYzDWnj0qD5tz52PI08u9qUvSVeUBeHw==} 2298 | dev: true 2299 | 2300 | /node-releases@2.0.14: 2301 | resolution: {integrity: sha512-y10wOWt8yZpqXmOgRo77WaHEmhYQYGNA6y421PKsKYWEK8aW+cqAphborZDhqfyKrbZEN92CN1X2KbafY2s7Yw==} 2302 | dev: true 2303 | 2304 | /normalize-path@3.0.0: 2305 | resolution: {integrity: sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==} 2306 | engines: {node: '>=0.10.0'} 2307 | dev: true 2308 | 2309 | /npm-run-path@4.0.1: 2310 | resolution: {integrity: sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==} 2311 | engines: {node: '>=8'} 2312 | dependencies: 2313 | path-key: 3.1.1 2314 | dev: true 2315 | 2316 | /object-inspect@1.13.1: 2317 | resolution: {integrity: sha512-5qoj1RUiKOMsCCNLV1CBiPYE10sziTsnmNxkAI/rZhiD63CF7IqdFGC/XzjWjpSgLf0LxXX3bDFIh0E18f6UhQ==} 2318 | 2319 | /on-finished@2.4.1: 2320 | resolution: {integrity: sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==} 2321 | engines: {node: '>= 0.8'} 2322 | dependencies: 2323 | ee-first: 1.1.1 2324 | dev: false 2325 | 2326 | /once@1.4.0: 2327 | resolution: {integrity: sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==} 2328 | dependencies: 2329 | wrappy: 1.0.2 2330 | dev: true 2331 | 2332 | /onetime@5.1.2: 2333 | resolution: {integrity: sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==} 2334 | engines: {node: '>=6'} 2335 | dependencies: 2336 | mimic-fn: 2.1.0 2337 | dev: true 2338 | 2339 | /p-limit@2.3.0: 2340 | resolution: {integrity: sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==} 2341 | engines: {node: '>=6'} 2342 | dependencies: 2343 | p-try: 2.2.0 2344 | dev: true 2345 | 2346 | /p-limit@3.1.0: 2347 | resolution: {integrity: sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==} 2348 | engines: {node: '>=10'} 2349 | dependencies: 2350 | yocto-queue: 0.1.0 2351 | dev: true 2352 | 2353 | /p-locate@4.1.0: 2354 | resolution: {integrity: sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==} 2355 | engines: {node: '>=8'} 2356 | dependencies: 2357 | p-limit: 2.3.0 2358 | dev: true 2359 | 2360 | /p-try@2.2.0: 2361 | resolution: {integrity: sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==} 2362 | engines: {node: '>=6'} 2363 | dev: true 2364 | 2365 | /parse-json@5.2.0: 2366 | resolution: {integrity: sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==} 2367 | engines: {node: '>=8'} 2368 | dependencies: 2369 | '@babel/code-frame': 7.23.5 2370 | error-ex: 1.3.2 2371 | json-parse-even-better-errors: 2.3.1 2372 | lines-and-columns: 1.2.4 2373 | dev: true 2374 | 2375 | /parseurl@1.3.3: 2376 | resolution: {integrity: sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==} 2377 | engines: {node: '>= 0.8'} 2378 | dev: false 2379 | 2380 | /path-exists@4.0.0: 2381 | resolution: {integrity: sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==} 2382 | engines: {node: '>=8'} 2383 | dev: true 2384 | 2385 | /path-is-absolute@1.0.1: 2386 | resolution: {integrity: sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==} 2387 | engines: {node: '>=0.10.0'} 2388 | dev: true 2389 | 2390 | /path-key@3.1.1: 2391 | resolution: {integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==} 2392 | engines: {node: '>=8'} 2393 | dev: true 2394 | 2395 | /path-parse@1.0.7: 2396 | resolution: {integrity: sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==} 2397 | dev: true 2398 | 2399 | /path-to-regexp@0.1.7: 2400 | resolution: {integrity: sha512-5DFkuoqlv1uYQKxy8omFBeJPQcdoE07Kv2sferDCrAq1ohOU+MSDswDIbnx3YAM60qIOnYa53wBhXW0EbMonrQ==} 2401 | dev: false 2402 | 2403 | /picocolors@1.0.0: 2404 | resolution: {integrity: sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==} 2405 | dev: true 2406 | 2407 | /picomatch@2.3.1: 2408 | resolution: {integrity: sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==} 2409 | engines: {node: '>=8.6'} 2410 | dev: true 2411 | 2412 | /pirates@4.0.6: 2413 | resolution: {integrity: sha512-saLsH7WeYYPiD25LDuLRRY/i+6HaPYr6G1OUlN39otzkSTxKnubR9RTxS3/Kk50s1g2JTgFwWQDQyplC5/SHZg==} 2414 | engines: {node: '>= 6'} 2415 | dev: true 2416 | 2417 | /pkg-dir@4.2.0: 2418 | resolution: {integrity: sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==} 2419 | engines: {node: '>=8'} 2420 | dependencies: 2421 | find-up: 4.1.0 2422 | dev: true 2423 | 2424 | /pretty-format@29.7.0: 2425 | resolution: {integrity: sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==} 2426 | engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} 2427 | dependencies: 2428 | '@jest/schemas': 29.6.3 2429 | ansi-styles: 5.2.0 2430 | react-is: 18.2.0 2431 | dev: true 2432 | 2433 | /prompts@2.4.2: 2434 | resolution: {integrity: sha512-NxNv/kLguCA7p3jE8oL2aEBsrJWgAakBpgmgK6lpPWV+WuOmY6r2/zbAVnP+T8bQlA0nzHXSJSJW0Hq7ylaD2Q==} 2435 | engines: {node: '>= 6'} 2436 | dependencies: 2437 | kleur: 3.0.3 2438 | sisteransi: 1.0.5 2439 | dev: true 2440 | 2441 | /proxy-addr@2.0.7: 2442 | resolution: {integrity: sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==} 2443 | engines: {node: '>= 0.10'} 2444 | dependencies: 2445 | forwarded: 0.2.0 2446 | ipaddr.js: 1.9.1 2447 | dev: false 2448 | 2449 | /pure-rand@6.0.4: 2450 | resolution: {integrity: sha512-LA0Y9kxMYv47GIPJy6MI84fqTd2HmYZI83W/kM/SkKfDlajnZYfmXFTxkbY+xSBPkLJxltMa9hIkmdc29eguMA==} 2451 | dev: true 2452 | 2453 | /qs@6.11.0: 2454 | resolution: {integrity: sha512-MvjoMCJwEarSbUYk5O+nmoSzSutSsTwF85zcHPQ9OrlFoZOYIjaqBAJIqIXjptyD5vThxGq52Xu/MaJzRkIk4Q==} 2455 | engines: {node: '>=0.6'} 2456 | dependencies: 2457 | side-channel: 1.0.4 2458 | dev: false 2459 | 2460 | /qs@6.11.2: 2461 | resolution: {integrity: sha512-tDNIz22aBzCDxLtVH++VnTfzxlfeK5CbqohpSqpJgj1Wg/cQbStNAz3NuqCs5vV+pjBsK4x4pN9HlVh7rcYRiA==} 2462 | engines: {node: '>=0.6'} 2463 | dependencies: 2464 | side-channel: 1.0.4 2465 | dev: true 2466 | 2467 | /range-parser@1.2.1: 2468 | resolution: {integrity: sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==} 2469 | engines: {node: '>= 0.6'} 2470 | dev: false 2471 | 2472 | /raw-body@2.5.1: 2473 | resolution: {integrity: sha512-qqJBtEyVgS0ZmPGdCFPWJ3FreoqvG4MVQln/kCgF7Olq95IbOp0/BWyMwbdtn4VTvkM8Y7khCQ2Xgk/tcrCXig==} 2474 | engines: {node: '>= 0.8'} 2475 | dependencies: 2476 | bytes: 3.1.2 2477 | http-errors: 2.0.0 2478 | iconv-lite: 0.4.24 2479 | unpipe: 1.0.0 2480 | dev: false 2481 | 2482 | /react-is@18.2.0: 2483 | resolution: {integrity: sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==} 2484 | dev: true 2485 | 2486 | /require-directory@2.1.1: 2487 | resolution: {integrity: sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==} 2488 | engines: {node: '>=0.10.0'} 2489 | dev: true 2490 | 2491 | /resolve-cwd@3.0.0: 2492 | resolution: {integrity: sha512-OrZaX2Mb+rJCpH/6CpSqt9xFVpN++x01XnN2ie9g6P5/3xelLAkXWVADpdz1IHD/KFfEXyE6V0U01OQ3UO2rEg==} 2493 | engines: {node: '>=8'} 2494 | dependencies: 2495 | resolve-from: 5.0.0 2496 | dev: true 2497 | 2498 | /resolve-from@5.0.0: 2499 | resolution: {integrity: sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==} 2500 | engines: {node: '>=8'} 2501 | dev: true 2502 | 2503 | /resolve.exports@2.0.2: 2504 | resolution: {integrity: sha512-X2UW6Nw3n/aMgDVy+0rSqgHlv39WZAlZrXCdnbyEiKm17DSqHX4MmQMaST3FbeWR5FTuRcUwYAziZajji0Y7mg==} 2505 | engines: {node: '>=10'} 2506 | dev: true 2507 | 2508 | /resolve@1.22.8: 2509 | resolution: {integrity: sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw==} 2510 | hasBin: true 2511 | dependencies: 2512 | is-core-module: 2.13.1 2513 | path-parse: 1.0.7 2514 | supports-preserve-symlinks-flag: 1.0.0 2515 | dev: true 2516 | 2517 | /safe-buffer@5.2.1: 2518 | resolution: {integrity: sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==} 2519 | dev: false 2520 | 2521 | /safer-buffer@2.1.2: 2522 | resolution: {integrity: sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==} 2523 | dev: false 2524 | 2525 | /semver@6.3.1: 2526 | resolution: {integrity: sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==} 2527 | hasBin: true 2528 | dev: true 2529 | 2530 | /semver@7.5.4: 2531 | resolution: {integrity: sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==} 2532 | engines: {node: '>=10'} 2533 | hasBin: true 2534 | dependencies: 2535 | lru-cache: 6.0.0 2536 | dev: true 2537 | 2538 | /send@0.18.0: 2539 | resolution: {integrity: sha512-qqWzuOjSFOuqPjFe4NOsMLafToQQwBSOEpS+FwEt3A2V3vKubTquT3vmLTQpFgMXp8AlFWFuP1qKaJZOtPpVXg==} 2540 | engines: {node: '>= 0.8.0'} 2541 | dependencies: 2542 | debug: 2.6.9 2543 | depd: 2.0.0 2544 | destroy: 1.2.0 2545 | encodeurl: 1.0.2 2546 | escape-html: 1.0.3 2547 | etag: 1.8.1 2548 | fresh: 0.5.2 2549 | http-errors: 2.0.0 2550 | mime: 1.6.0 2551 | ms: 2.1.3 2552 | on-finished: 2.4.1 2553 | range-parser: 1.2.1 2554 | statuses: 2.0.1 2555 | transitivePeerDependencies: 2556 | - supports-color 2557 | dev: false 2558 | 2559 | /serve-static@1.15.0: 2560 | resolution: {integrity: sha512-XGuRDNjXUijsUL0vl6nSD7cwURuzEgglbOaFuZM9g3kwDXOWVTck0jLzjPzGD+TazWbboZYu52/9/XPdUgne9g==} 2561 | engines: {node: '>= 0.8.0'} 2562 | dependencies: 2563 | encodeurl: 1.0.2 2564 | escape-html: 1.0.3 2565 | parseurl: 1.3.3 2566 | send: 0.18.0 2567 | transitivePeerDependencies: 2568 | - supports-color 2569 | dev: false 2570 | 2571 | /set-function-length@1.2.0: 2572 | resolution: {integrity: sha512-4DBHDoyHlM1IRPGYcoxexgh67y4ueR53FKV1yyxwFMY7aCqcN/38M1+SwZ/qJQ8iLv7+ck385ot4CcisOAPT9w==} 2573 | engines: {node: '>= 0.4'} 2574 | dependencies: 2575 | define-data-property: 1.1.1 2576 | function-bind: 1.1.2 2577 | get-intrinsic: 1.2.2 2578 | gopd: 1.0.1 2579 | has-property-descriptors: 1.0.1 2580 | 2581 | /setprototypeof@1.2.0: 2582 | resolution: {integrity: sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==} 2583 | dev: false 2584 | 2585 | /shebang-command@2.0.0: 2586 | resolution: {integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==} 2587 | engines: {node: '>=8'} 2588 | dependencies: 2589 | shebang-regex: 3.0.0 2590 | dev: true 2591 | 2592 | /shebang-regex@3.0.0: 2593 | resolution: {integrity: sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==} 2594 | engines: {node: '>=8'} 2595 | dev: true 2596 | 2597 | /side-channel@1.0.4: 2598 | resolution: {integrity: sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==} 2599 | dependencies: 2600 | call-bind: 1.0.5 2601 | get-intrinsic: 1.2.2 2602 | object-inspect: 1.13.1 2603 | 2604 | /signal-exit@3.0.7: 2605 | resolution: {integrity: sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==} 2606 | dev: true 2607 | 2608 | /sisteransi@1.0.5: 2609 | resolution: {integrity: sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==} 2610 | dev: true 2611 | 2612 | /slash@3.0.0: 2613 | resolution: {integrity: sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==} 2614 | engines: {node: '>=8'} 2615 | dev: true 2616 | 2617 | /source-map-support@0.5.13: 2618 | resolution: {integrity: sha512-SHSKFHadjVA5oR4PPqhtAVdcBWwRYVd6g6cAXnIbRiIwc2EhPrTuKUBdSLvlEKyIP3GCf89fltvcZiP9MMFA1w==} 2619 | dependencies: 2620 | buffer-from: 1.1.2 2621 | source-map: 0.6.1 2622 | dev: true 2623 | 2624 | /source-map@0.6.1: 2625 | resolution: {integrity: sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==} 2626 | engines: {node: '>=0.10.0'} 2627 | dev: true 2628 | 2629 | /sprintf-js@1.0.3: 2630 | resolution: {integrity: sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==} 2631 | dev: true 2632 | 2633 | /stack-utils@2.0.6: 2634 | resolution: {integrity: sha512-XlkWvfIm6RmsWtNJx+uqtKLS8eqFbxUg0ZzLXqY0caEy9l7hruX8IpiDnjsLavoBgqCCR71TqWO8MaXYheJ3RQ==} 2635 | engines: {node: '>=10'} 2636 | dependencies: 2637 | escape-string-regexp: 2.0.0 2638 | dev: true 2639 | 2640 | /statuses@2.0.1: 2641 | resolution: {integrity: sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==} 2642 | engines: {node: '>= 0.8'} 2643 | dev: false 2644 | 2645 | /string-length@4.0.2: 2646 | resolution: {integrity: sha512-+l6rNN5fYHNhZZy41RXsYptCjA2Igmq4EG7kZAYFQI1E1VTXarr6ZPXBg6eq7Y6eK4FEhY6AJlyuFIb/v/S0VQ==} 2647 | engines: {node: '>=10'} 2648 | dependencies: 2649 | char-regex: 1.0.2 2650 | strip-ansi: 6.0.1 2651 | dev: true 2652 | 2653 | /string-width@4.2.3: 2654 | resolution: {integrity: sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==} 2655 | engines: {node: '>=8'} 2656 | dependencies: 2657 | emoji-regex: 8.0.0 2658 | is-fullwidth-code-point: 3.0.0 2659 | strip-ansi: 6.0.1 2660 | dev: true 2661 | 2662 | /strip-ansi@6.0.1: 2663 | resolution: {integrity: sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==} 2664 | engines: {node: '>=8'} 2665 | dependencies: 2666 | ansi-regex: 5.0.1 2667 | dev: true 2668 | 2669 | /strip-bom@4.0.0: 2670 | resolution: {integrity: sha512-3xurFv5tEgii33Zi8Jtp55wEIILR9eh34FAW00PZf+JnSsTmV/ioewSgQl97JHvgjoRGwPShsWm+IdrxB35d0w==} 2671 | engines: {node: '>=8'} 2672 | dev: true 2673 | 2674 | /strip-final-newline@2.0.0: 2675 | resolution: {integrity: sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==} 2676 | engines: {node: '>=6'} 2677 | dev: true 2678 | 2679 | /strip-json-comments@3.1.1: 2680 | resolution: {integrity: sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==} 2681 | engines: {node: '>=8'} 2682 | dev: true 2683 | 2684 | /superagent@8.1.2: 2685 | resolution: {integrity: sha512-6WTxW1EB6yCxV5VFOIPQruWGHqc3yI7hEmZK6h+pyk69Lk/Ut7rLUY6W/ONF2MjBuGjvmMiIpsrVJ2vjrHlslA==} 2686 | engines: {node: '>=6.4.0 <13 || >=14'} 2687 | dependencies: 2688 | component-emitter: 1.3.1 2689 | cookiejar: 2.1.4 2690 | debug: 4.3.4 2691 | fast-safe-stringify: 2.1.1 2692 | form-data: 4.0.0 2693 | formidable: 2.1.2 2694 | methods: 1.1.2 2695 | mime: 2.6.0 2696 | qs: 6.11.2 2697 | semver: 7.5.4 2698 | transitivePeerDependencies: 2699 | - supports-color 2700 | dev: true 2701 | 2702 | /supertest@6.3.4: 2703 | resolution: {integrity: sha512-erY3HFDG0dPnhw4U+udPfrzXa4xhSG+n4rxfRuZWCUvjFWwKl+OxWf/7zk50s84/fAAs7vf5QAb9uRa0cCykxw==} 2704 | engines: {node: '>=6.4.0'} 2705 | dependencies: 2706 | methods: 1.1.2 2707 | superagent: 8.1.2 2708 | transitivePeerDependencies: 2709 | - supports-color 2710 | dev: true 2711 | 2712 | /supports-color@5.5.0: 2713 | resolution: {integrity: sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==} 2714 | engines: {node: '>=4'} 2715 | dependencies: 2716 | has-flag: 3.0.0 2717 | dev: true 2718 | 2719 | /supports-color@7.2.0: 2720 | resolution: {integrity: sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==} 2721 | engines: {node: '>=8'} 2722 | dependencies: 2723 | has-flag: 4.0.0 2724 | dev: true 2725 | 2726 | /supports-color@8.1.1: 2727 | resolution: {integrity: sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==} 2728 | engines: {node: '>=10'} 2729 | dependencies: 2730 | has-flag: 4.0.0 2731 | dev: true 2732 | 2733 | /supports-preserve-symlinks-flag@1.0.0: 2734 | resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==} 2735 | engines: {node: '>= 0.4'} 2736 | dev: true 2737 | 2738 | /test-exclude@6.0.0: 2739 | resolution: {integrity: sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w==} 2740 | engines: {node: '>=8'} 2741 | dependencies: 2742 | '@istanbuljs/schema': 0.1.3 2743 | glob: 7.2.3 2744 | minimatch: 3.1.2 2745 | dev: true 2746 | 2747 | /tmpl@1.0.5: 2748 | resolution: {integrity: sha512-3f0uOEAQwIqGuWW2MVzYg8fV/QNnc/IpuJNG837rLuczAaLVHslWHZQj4IGiEl5Hs3kkbhwL9Ab7Hrsmuj+Smw==} 2749 | dev: true 2750 | 2751 | /to-fast-properties@2.0.0: 2752 | resolution: {integrity: sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog==} 2753 | engines: {node: '>=4'} 2754 | dev: true 2755 | 2756 | /to-regex-range@5.0.1: 2757 | resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==} 2758 | engines: {node: '>=8.0'} 2759 | dependencies: 2760 | is-number: 7.0.0 2761 | dev: true 2762 | 2763 | /toidentifier@1.0.1: 2764 | resolution: {integrity: sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==} 2765 | engines: {node: '>=0.6'} 2766 | dev: false 2767 | 2768 | /ts-jest@29.1.5(@babel/core@7.23.9)(jest@29.7.0)(typescript@5.5.3): 2769 | resolution: {integrity: sha512-UuClSYxM7byvvYfyWdFI+/2UxMmwNyJb0NPkZPQE2hew3RurV7l7zURgOHAd/1I1ZdPpe3GUsXNXAcN8TFKSIg==} 2770 | engines: {node: ^14.15.0 || ^16.10.0 || ^18.0.0 || >=20.0.0} 2771 | hasBin: true 2772 | peerDependencies: 2773 | '@babel/core': '>=7.0.0-beta.0 <8' 2774 | '@jest/transform': ^29.0.0 2775 | '@jest/types': ^29.0.0 2776 | babel-jest: ^29.0.0 2777 | esbuild: '*' 2778 | jest: ^29.0.0 2779 | typescript: '>=4.3 <6' 2780 | peerDependenciesMeta: 2781 | '@babel/core': 2782 | optional: true 2783 | '@jest/transform': 2784 | optional: true 2785 | '@jest/types': 2786 | optional: true 2787 | babel-jest: 2788 | optional: true 2789 | esbuild: 2790 | optional: true 2791 | dependencies: 2792 | '@babel/core': 7.23.9 2793 | bs-logger: 0.2.6 2794 | fast-json-stable-stringify: 2.1.0 2795 | jest: 29.7.0 2796 | jest-util: 29.7.0 2797 | json5: 2.2.3 2798 | lodash.memoize: 4.1.2 2799 | make-error: 1.3.6 2800 | semver: 7.5.4 2801 | typescript: 5.5.3 2802 | yargs-parser: 21.1.1 2803 | dev: true 2804 | 2805 | /type-detect@4.0.8: 2806 | resolution: {integrity: sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==} 2807 | engines: {node: '>=4'} 2808 | dev: true 2809 | 2810 | /type-fest@0.21.3: 2811 | resolution: {integrity: sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==} 2812 | engines: {node: '>=10'} 2813 | dev: true 2814 | 2815 | /type-is@1.6.18: 2816 | resolution: {integrity: sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==} 2817 | engines: {node: '>= 0.6'} 2818 | dependencies: 2819 | media-typer: 0.3.0 2820 | mime-types: 2.1.35 2821 | dev: false 2822 | 2823 | /typescript@5.5.3: 2824 | resolution: {integrity: sha512-/hreyEujaB0w76zKo6717l3L0o/qEUtRgdvUBvlkhoWeOVMjMuHNHk0BRBzikzuGDqNmPQbg5ifMEqsHLiIUcQ==} 2825 | engines: {node: '>=14.17'} 2826 | hasBin: true 2827 | dev: true 2828 | 2829 | /undici-types@5.26.5: 2830 | resolution: {integrity: sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==} 2831 | dev: true 2832 | 2833 | /unpipe@1.0.0: 2834 | resolution: {integrity: sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==} 2835 | engines: {node: '>= 0.8'} 2836 | dev: false 2837 | 2838 | /update-browserslist-db@1.0.13(browserslist@4.22.3): 2839 | resolution: {integrity: sha512-xebP81SNcPuNpPP3uzeW1NYXxI3rxyJzF3pD6sH4jE7o/IX+WtSpwnVU+qIsDPyk0d3hmFQ7mjqc6AtV604hbg==} 2840 | hasBin: true 2841 | peerDependencies: 2842 | browserslist: '>= 4.21.0' 2843 | dependencies: 2844 | browserslist: 4.22.3 2845 | escalade: 3.1.1 2846 | picocolors: 1.0.0 2847 | dev: true 2848 | 2849 | /utils-merge@1.0.1: 2850 | resolution: {integrity: sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==} 2851 | engines: {node: '>= 0.4.0'} 2852 | dev: false 2853 | 2854 | /v8-to-istanbul@9.2.0: 2855 | resolution: {integrity: sha512-/EH/sDgxU2eGxajKdwLCDmQ4FWq+kpi3uCmBGpw1xJtnAxEjlD8j8PEiGWpCIMIs3ciNAgH0d3TTJiUkYzyZjA==} 2856 | engines: {node: '>=10.12.0'} 2857 | dependencies: 2858 | '@jridgewell/trace-mapping': 0.3.22 2859 | '@types/istanbul-lib-coverage': 2.0.6 2860 | convert-source-map: 2.0.0 2861 | dev: true 2862 | 2863 | /vary@1.1.2: 2864 | resolution: {integrity: sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==} 2865 | engines: {node: '>= 0.8'} 2866 | dev: false 2867 | 2868 | /walker@1.0.8: 2869 | resolution: {integrity: sha512-ts/8E8l5b7kY0vlWLewOkDXMmPdLcVV4GmOQLyxuSswIJsweeFZtAsMF7k1Nszz+TYBQrlYRmzOnr398y1JemQ==} 2870 | dependencies: 2871 | makeerror: 1.0.12 2872 | dev: true 2873 | 2874 | /which@2.0.2: 2875 | resolution: {integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==} 2876 | engines: {node: '>= 8'} 2877 | hasBin: true 2878 | dependencies: 2879 | isexe: 2.0.0 2880 | dev: true 2881 | 2882 | /wrap-ansi@7.0.0: 2883 | resolution: {integrity: sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==} 2884 | engines: {node: '>=10'} 2885 | dependencies: 2886 | ansi-styles: 4.3.0 2887 | string-width: 4.2.3 2888 | strip-ansi: 6.0.1 2889 | dev: true 2890 | 2891 | /wrappy@1.0.2: 2892 | resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==} 2893 | dev: true 2894 | 2895 | /write-file-atomic@4.0.2: 2896 | resolution: {integrity: sha512-7KxauUdBmSdWnmpaGFg+ppNjKF8uNLry8LyzjauQDOVONfFLNKrKvQOxZ/VuTIcS/gge/YNahf5RIIQWTSarlg==} 2897 | engines: {node: ^12.13.0 || ^14.15.0 || >=16.0.0} 2898 | dependencies: 2899 | imurmurhash: 0.1.4 2900 | signal-exit: 3.0.7 2901 | dev: true 2902 | 2903 | /y18n@5.0.8: 2904 | resolution: {integrity: sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==} 2905 | engines: {node: '>=10'} 2906 | dev: true 2907 | 2908 | /yallist@3.1.1: 2909 | resolution: {integrity: sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==} 2910 | dev: true 2911 | 2912 | /yallist@4.0.0: 2913 | resolution: {integrity: sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==} 2914 | dev: true 2915 | 2916 | /yargs-parser@21.1.1: 2917 | resolution: {integrity: sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==} 2918 | engines: {node: '>=12'} 2919 | dev: true 2920 | 2921 | /yargs@17.7.2: 2922 | resolution: {integrity: sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==} 2923 | engines: {node: '>=12'} 2924 | dependencies: 2925 | cliui: 8.0.1 2926 | escalade: 3.1.1 2927 | get-caller-file: 2.0.5 2928 | require-directory: 2.1.1 2929 | string-width: 4.2.3 2930 | y18n: 5.0.8 2931 | yargs-parser: 21.1.1 2932 | dev: true 2933 | 2934 | /yocto-queue@0.1.0: 2935 | resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==} 2936 | engines: {node: '>=10'} 2937 | dev: true 2938 | 2939 | /zod@3.22.4: 2940 | resolution: {integrity: sha512-iC+8Io04lddc+mVqQ9AZ7OQ2MrUKGN+oIQyq1vemgt46jwCwLfhq7/pwnBnNXXXZb8VTVLKwp9EDkx+ryxIWmg==} 2941 | dev: false 2942 | -------------------------------------------------------------------------------- /src/index.ts: -------------------------------------------------------------------------------- 1 | import express from 'express' 2 | import type { Request as ExpressDefaultRequest, Response, NextFunction } from 'express' 3 | import { 4 | baseObjectInputType, 5 | baseObjectOutputType, 6 | objectUtil, 7 | z, 8 | ZodError, 9 | ZodObject, 10 | ZodRawShape, 11 | ZodTypeAny, 12 | } from 'zod' 13 | import { ErrorReturnType, HTTPError, HTTPStatus, HTTPStatusText } from './utils/error.js' 14 | export { HTTPError, HTTPStatus, HTTPStatusText } from './utils/error.js' 15 | 16 | export type CustomRequest< 17 | RequestType, 18 | ReqBody = any, 19 | ReqQuery = any, 20 | ReqHeaders = any, 21 | ReqParams = any 22 | > = Omit & { 23 | body: ReqBody 24 | query: ReqQuery 25 | headers: ReqHeaders 26 | params: ReqParams 27 | } 28 | 29 | type Middleware< 30 | ReqType = ExpressDefaultRequest, 31 | ReqBody = any, 32 | ReqQuery = any, 33 | ReqHeaders = any, 34 | ReqParams = any, 35 | PrevContext = {}, 36 | NewContext = {} 37 | > = ( 38 | req: CustomRequest, 39 | res: Response, 40 | next: NextFunction, 41 | prevContext: PrevContext 42 | ) => NonNullable | Promise> | void | Promise 43 | 44 | class ValidationError extends Error { 45 | public statusCode: number 46 | public errors: ErrorReturnType['errors'] 47 | public location: ErrorReturnType['location'] 48 | 49 | constructor(zodError: ZodError, location: string) { 50 | super('Validation failed') 51 | 52 | // HTTP status code for validation errors 53 | this.statusCode = 400 54 | 55 | // Extract relevant error details from ZodError 56 | this.errors = JSON.parse(zodError.message).map((el: any) => ({ 57 | message: el.message, 58 | path: el.path, 59 | })) 60 | 61 | this.location = location as ErrorReturnType['location'] 62 | } 63 | } 64 | 65 | export type HandlerReturnType< 66 | TransformedData, 67 | ReqBody, 68 | ReqQuery, 69 | ReqHeaders, 70 | ReqParams, 71 | ResolvedData 72 | > = express.RequestHandler & { 73 | types: { 74 | response: TransformedData 75 | request: { 76 | body: ReqBody 77 | query: ReqQuery 78 | headers: ReqHeaders 79 | params: ReqParams 80 | } 81 | } 82 | /** 83 | * @deprecated Use response instead 84 | * */ 85 | transformedData: TransformedData 86 | } 87 | 88 | export class Handler< 89 | /** 90 | * Set this to the type of the data that will be returned by the transformer function. 91 | */ 92 | GlobalOutputData extends {}, 93 | RequestType = ExpressDefaultRequest, 94 | ReqBody = any, 95 | ReqQuery = any, 96 | ReqHeaders = any, 97 | ReqParams = any, 98 | ResolvedData = any, 99 | Context = {} 100 | > { 101 | private _resolver?: ( 102 | req: CustomRequest, 103 | res: Response, 104 | context: Context 105 | ) => any 106 | private _transformer?: ( 107 | data: ResolvedData, 108 | res: Response 109 | ) => GlobalOutputData | Promise 110 | 111 | private _chain: (( 112 | req: CustomRequest, 113 | res: Response, 114 | next: NextFunction, 115 | context: Partial 116 | ) => Partial | Promise>)[] = [] 117 | 118 | /** 119 | * Function to add a single middleware to the chain 120 | * @param mw The middleware to add 121 | */ 122 | middleware( 123 | mw: Middleware 124 | ): Handler< 125 | GlobalOutputData, 126 | RequestType, 127 | ReqBody, 128 | ReqQuery, 129 | ReqHeaders, 130 | ReqParams, 131 | ResolvedData, 132 | // existing context merged with new context. If new context has a key that already exists in existing context, the new context key will be used as type 133 | Omit & NewContext 134 | > { 135 | this._chain.push(async (req, res, next, context) => { 136 | const contextData = (await mw(req, res, next, context as Context)) as Partial 137 | 138 | if (contextData === undefined || contextData === null) { 139 | // return old context if new context is undefined or null 140 | return context 141 | } 142 | return contextData 143 | }) 144 | return this as any 145 | } 146 | 147 | /** 148 | * 149 | * @param type the location of the data to validate. Can be "body", "query", "headers", or "params" 150 | * @param schema the schema to validate against. Can be a zod object or a raw object containing zod types 151 | */ 152 | validate< 153 | T extends 'body' | 'query' | 'headers' | 'params', 154 | K extends ZodRawShape, 155 | // copied from zod/lib/types.d.ts objectType. Adjusted to K instead of T 156 | U extends ZodObject< 157 | K, 158 | 'strip', 159 | ZodTypeAny, 160 | { 161 | [k_1 in keyof objectUtil.addQuestionMarks< 162 | baseObjectOutputType, 163 | { 164 | [k in keyof baseObjectOutputType]: undefined extends baseObjectOutputType[k] 165 | ? never 166 | : k 167 | }[keyof K] 168 | >]: objectUtil.addQuestionMarks< 169 | baseObjectOutputType, 170 | { 171 | [k in keyof baseObjectOutputType]: undefined extends baseObjectOutputType[k] 172 | ? never 173 | : k 174 | }[keyof K] 175 | >[k_1] 176 | }, 177 | { 178 | [k_2 in keyof baseObjectInputType]: baseObjectInputType[k_2] 179 | } 180 | > 181 | >( 182 | type: T, 183 | schema: K | U 184 | ): Handler< 185 | GlobalOutputData, 186 | RequestType, 187 | T extends 'body' ? z.infer : ReqBody, 188 | T extends 'query' ? z.infer : ReqQuery, 189 | T extends 'headers' ? z.infer : ReqHeaders, 190 | T extends 'params' ? z.infer : ReqParams 191 | > { 192 | // validation logic here 193 | const validationMiddleware = async ( 194 | req: CustomRequest, 195 | res: Response, 196 | next: NextFunction 197 | ) => { 198 | try { 199 | // if the request body is not an object, don't validate it 200 | if (typeof req[type] !== 'object') { 201 | throw new HTTPError(400, 'Invalid request body (not an object)') 202 | } 203 | 204 | const value = req[type] 205 | 206 | function isZodObject(object: K | U): object is U { 207 | return '_def' in object 208 | } 209 | 210 | // if it's already an zod object, just use it. Else, make it a zod object first 211 | if (isZodObject(schema)) { 212 | // Overwrite the object inside request with the validated object to allow for transformations and refinements through zod 213 | // @ts-ignore 214 | req[type] = await schema.parseAsync(value) 215 | next() 216 | } else { 217 | // Have to ignore the following because of an unresolved type issue. Still works as expected 218 | // @ts-ignore 219 | const combinedSchema: U = z.object(schema) 220 | 221 | // Overwrite the object inside request with the validated object to allow for transformations and refinements through zod 222 | // @ts-ignore 223 | req[type] = await combinedSchema.parseAsync(value) 224 | } 225 | } catch (err) { 226 | if (err instanceof ZodError) { 227 | throw new ValidationError(err, type) 228 | } 229 | throw err 230 | } 231 | 232 | return {} 233 | } 234 | 235 | this._chain.push(validationMiddleware) 236 | return this as any 237 | } 238 | 239 | /** 240 | * Define the resolver function for the handler. The resolver returns data that needs to be picked up by the transformer function 241 | * @param fn The function to run after all validations and middlewares have been processed 242 | */ 243 | resolve( 244 | fn: ( 245 | req: CustomRequest, 246 | res: Response, 247 | context: Context 248 | ) => Data | Promise 249 | ): Handler< 250 | GlobalOutputData, 251 | RequestType, 252 | ReqBody, 253 | ReqQuery, 254 | ReqHeaders, 255 | ReqParams, 256 | Data, 257 | Context 258 | > { 259 | this._resolver = fn 260 | return this as any 261 | } 262 | /** 263 | * Define the transformer function for the handler. The transformer function receives the data returned by the resolver and is responsible for sending the response. Useful for creating DTOs 264 | * @param fn The function to run after the resolver has been run. This function is responsible for sending the response 265 | * @returns 266 | */ 267 | transform( 268 | fn: (data: ResolvedData, res: Response) => T | Promise 269 | ): Handler { 270 | this._transformer = fn 271 | 272 | // @ts-ignore 273 | return this 274 | } 275 | 276 | getOutputTypes(): GlobalOutputData { 277 | return {} as GlobalOutputData 278 | } 279 | 280 | express(): HandlerReturnType< 281 | GlobalOutputData, 282 | ReqBody, 283 | ReqQuery, 284 | ReqHeaders, 285 | ReqParams, 286 | ResolvedData 287 | > { 288 | const runner = async ( 289 | req: CustomRequest, 290 | res: Response, 291 | next: NextFunction 292 | ) => { 293 | let index = 0 294 | 295 | let context: Context = {} as Context 296 | 297 | const runNextChainItem = async () => { 298 | // If there are no more middlewares to run, run the resolver 299 | if (index >= this._chain.length) { 300 | // All middlewares and validations have been processed 301 | try { 302 | const result = await this._resolver!(req, res, context) 303 | if (this._transformer) { 304 | const transformerValue = await this._transformer(result, res) 305 | if (transformerValue !== undefined) { 306 | if ( 307 | typeof transformerValue === 'object' && 308 | transformerValue !== null 309 | ) { 310 | res.json(transformerValue) 311 | } else { 312 | res.send(transformerValue) 313 | } 314 | } 315 | return 316 | } else { 317 | return 318 | } 319 | } catch (err) { 320 | if (err instanceof HTTPError) { 321 | return res.status(err.status).json({ 322 | errors: [{ message: err.message }], 323 | type: HTTPStatusText[err.status as HTTPStatus], 324 | } satisfies ErrorReturnType) 325 | } else { 326 | next(err) 327 | return 328 | } 329 | } 330 | } 331 | 332 | // Otherwise, run the next middleware 333 | const currentMiddleware = this._chain[index++] 334 | try { 335 | const returnedContextValue = await currentMiddleware?.( 336 | req, 337 | res, 338 | (err) => { 339 | if (err instanceof Error) { 340 | throw err 341 | } 342 | }, 343 | context 344 | ) 345 | 346 | // only append object values to context 347 | if ( 348 | returnedContextValue !== undefined && 349 | returnedContextValue !== null && 350 | typeof returnedContextValue === 'object' && 351 | !Array.isArray(returnedContextValue) 352 | ) { 353 | context = { ...context, ...returnedContextValue } 354 | } 355 | 356 | // If a middleware has already sent a response, don't run the next middleware 357 | if (res.headersSent) return 358 | 359 | await runNextChainItem() 360 | } catch (err) { 361 | if (err instanceof ValidationError) { 362 | return res.status(err.statusCode).json({ 363 | errors: err.errors, 364 | location: err.location, 365 | type: HTTPStatusText[err.statusCode as HTTPStatus], 366 | } satisfies ErrorReturnType) 367 | } else if (err instanceof HTTPError) { 368 | return res.status(err.status).json({ 369 | errors: [{ message: err.message }], 370 | type: HTTPStatusText[err.status as HTTPStatus], 371 | } satisfies ErrorReturnType) 372 | } else { 373 | // call the native next express error handler 374 | next(err) 375 | return 376 | } 377 | } 378 | } 379 | 380 | await runNextChainItem() 381 | } 382 | 383 | return runner as unknown as HandlerReturnType< 384 | GlobalOutputData, 385 | ReqBody, 386 | ReqQuery, 387 | ReqHeaders, 388 | ReqParams, 389 | ResolvedData 390 | > 391 | } 392 | } 393 | -------------------------------------------------------------------------------- /src/utils/error.ts: -------------------------------------------------------------------------------- 1 | export enum HTTPStatus { 2 | OK = 200, 3 | CREATED = 201, 4 | ACCEPTED = 202, 5 | NON_AUTHORITATIVE_INFORMATION = 203, 6 | NO_CONTENT = 204, 7 | RESET_CONTENT = 205, 8 | PARTIAL_CONTENT = 206, 9 | MULTI_STATUS = 207, 10 | ALREADY_REPORTED = 208, 11 | IM_USED = 226, 12 | 13 | MULTIPLE_CHOICES = 300, 14 | MOVED_PERMANENTLY = 301, 15 | FOUND = 302, 16 | SEE_OTHER = 303, 17 | NOT_MODIFIED = 304, 18 | USE_PROXY = 305, 19 | SWITCH_PROXY = 306, 20 | TEMPORARY_REDIRECT = 307, 21 | PERMANENT_REDIRECT = 308, 22 | 23 | BAD_REQUEST = 400, 24 | UNAUTHORIZED = 401, 25 | PAYMENT_REQUIRED = 402, 26 | FORBIDDEN = 403, 27 | NOT_FOUND = 404, 28 | METHOD_NOT_ALLOWED = 405, 29 | NOT_ACCEPTABLE = 406, 30 | PROXY_AUTHENTICATION_REQUIRED = 407, 31 | REQUEST_TIMEOUT = 408, 32 | CONFLICT = 409, 33 | GONE = 410, 34 | LENGTH_REQUIRED = 411, 35 | PRECONDITION_FAILED = 412, 36 | PAYLOAD_TOO_LARGE = 413, 37 | URI_TOO_LONG = 414, 38 | UNSUPPORTED_MEDIA_TYPE = 415, 39 | RANGE_NOT_SATISFIABLE = 416, 40 | EXPECTATION_FAILED = 417, 41 | I_AM_A_TEAPOT = 418, 42 | MISDIRECTED_REQUEST = 421, 43 | UNPROCESSABLE_ENTITY = 422, 44 | LOCKED = 423, 45 | FAILED_DEPENDENCY = 424, 46 | TOO_EARLY = 425, 47 | UPGRADE_REQUIRED = 426, 48 | PRECONDITION_REQUIRED = 428, 49 | TOO_MANY_REQUESTS = 429, 50 | REQUEST_HEADER_FIELDS_TOO_LARGE = 431, 51 | UNAVAILABLE_FOR_LEGAL_REASONS = 451, 52 | 53 | INTERNAL_SERVER_ERROR = 500, 54 | NOT_IMPLEMENTED = 501, 55 | BAD_GATEWAY = 502, 56 | SERVICE_UNAVAILABLE = 503, 57 | GATEWAY_TIMEOUT = 504, 58 | HTTP_VERSION_NOT_SUPPORTED = 505, 59 | VARIANT_ALSO_NEGOTIATES = 506, 60 | INSUFFICIENT_STORAGE = 507, 61 | LOOP_DETECTED = 508, 62 | NOT_EXTENDED = 510, 63 | NETWORK_AUTHENTICATION_REQUIRED = 511, 64 | } 65 | 66 | export const HTTPStatusText: Record = { 67 | [HTTPStatus.OK]: 'OK', 68 | [HTTPStatus.CREATED]: 'Created', 69 | [HTTPStatus.ACCEPTED]: 'Accepted', 70 | [HTTPStatus.NON_AUTHORITATIVE_INFORMATION]: 'Non-Authoritative Information', 71 | [HTTPStatus.NO_CONTENT]: 'No Content', 72 | [HTTPStatus.RESET_CONTENT]: 'Reset Content', 73 | [HTTPStatus.PARTIAL_CONTENT]: 'Partial Content', 74 | [HTTPStatus.MULTI_STATUS]: 'Multi-Status', 75 | [HTTPStatus.ALREADY_REPORTED]: 'Already Reported', 76 | [HTTPStatus.IM_USED]: 'IM Used', 77 | 78 | [HTTPStatus.MULTIPLE_CHOICES]: 'Multiple Choices', 79 | [HTTPStatus.MOVED_PERMANENTLY]: 'Moved Permanently', 80 | [HTTPStatus.FOUND]: 'Found', 81 | [HTTPStatus.SEE_OTHER]: 'See Other', 82 | [HTTPStatus.NOT_MODIFIED]: 'Not Modified', 83 | [HTTPStatus.USE_PROXY]: 'Use Proxy', 84 | [HTTPStatus.SWITCH_PROXY]: 'Switch Proxy', 85 | [HTTPStatus.TEMPORARY_REDIRECT]: 'Temporary Redirect', 86 | [HTTPStatus.PERMANENT_REDIRECT]: 'Permanent Redirect', 87 | 88 | [HTTPStatus.BAD_REQUEST]: 'Bad Request', 89 | [HTTPStatus.UNAUTHORIZED]: 'Unauthorized', 90 | [HTTPStatus.PAYMENT_REQUIRED]: 'Payment Required', 91 | [HTTPStatus.FORBIDDEN]: 'Forbidden', 92 | [HTTPStatus.NOT_FOUND]: 'Not Found', 93 | [HTTPStatus.METHOD_NOT_ALLOWED]: 'Method Not Allowed', 94 | [HTTPStatus.NOT_ACCEPTABLE]: 'Not Acceptable', 95 | [HTTPStatus.PROXY_AUTHENTICATION_REQUIRED]: 'Proxy Authentication Required', 96 | [HTTPStatus.REQUEST_TIMEOUT]: 'Request Timeout', 97 | [HTTPStatus.CONFLICT]: 'Conflict', 98 | [HTTPStatus.GONE]: 'Gone', 99 | [HTTPStatus.LENGTH_REQUIRED]: 'Length Required', 100 | [HTTPStatus.PRECONDITION_FAILED]: 'Precondition Failed', 101 | [HTTPStatus.PAYLOAD_TOO_LARGE]: 'Payload Too Large', 102 | [HTTPStatus.URI_TOO_LONG]: 'URI Too Long', 103 | [HTTPStatus.UNSUPPORTED_MEDIA_TYPE]: 'Unsupported Media Type', 104 | [HTTPStatus.RANGE_NOT_SATISFIABLE]: 'Range Not Satisfiable', 105 | [HTTPStatus.EXPECTATION_FAILED]: 'Expectation Failed', 106 | [HTTPStatus.I_AM_A_TEAPOT]: "I'm a teapot", 107 | [HTTPStatus.MISDIRECTED_REQUEST]: 'Misdirected Request', 108 | [HTTPStatus.UNPROCESSABLE_ENTITY]: 'Unprocessable Entity', 109 | [HTTPStatus.LOCKED]: 'Locked', 110 | [HTTPStatus.FAILED_DEPENDENCY]: 'Failed Dependency', 111 | [HTTPStatus.TOO_EARLY]: 'Too Early', 112 | [HTTPStatus.UPGRADE_REQUIRED]: 'Upgrade Required', 113 | [HTTPStatus.PRECONDITION_REQUIRED]: 'Precondition Required', 114 | [HTTPStatus.TOO_MANY_REQUESTS]: 'Too Many Requests', 115 | [HTTPStatus.REQUEST_HEADER_FIELDS_TOO_LARGE]: 'Request Header Fields Too Large', 116 | [HTTPStatus.UNAVAILABLE_FOR_LEGAL_REASONS]: 'Unavailable For Legal Reasons', 117 | 118 | [HTTPStatus.INTERNAL_SERVER_ERROR]: 'Internal Server Error', 119 | [HTTPStatus.NOT_IMPLEMENTED]: 'Not Implemented', 120 | [HTTPStatus.BAD_GATEWAY]: 'Bad Gateway', 121 | [HTTPStatus.SERVICE_UNAVAILABLE]: 'Service Unavailable', 122 | [HTTPStatus.GATEWAY_TIMEOUT]: 'Gateway Timeout', 123 | [HTTPStatus.HTTP_VERSION_NOT_SUPPORTED]: 'HTTP Version Not Supported', 124 | [HTTPStatus.VARIANT_ALSO_NEGOTIATES]: 'Variant Also Negotiates', 125 | [HTTPStatus.INSUFFICIENT_STORAGE]: 'Insufficient Storage', 126 | [HTTPStatus.LOOP_DETECTED]: 'Loop Detected', 127 | [HTTPStatus.NOT_EXTENDED]: 'Not Extended', 128 | [HTTPStatus.NETWORK_AUTHENTICATION_REQUIRED]: 'Network Authentication Required', 129 | } 130 | 131 | export class HTTPError extends Error { 132 | constructor(public readonly status: number, message: string) { 133 | super(message) 134 | } 135 | } 136 | 137 | export type ErrorReturnType = { 138 | errors: { 139 | message: string 140 | path?: (string | number)[] 141 | }[] 142 | location?: 'query' | 'params' | 'body' | 'headers' 143 | type: string 144 | } 145 | -------------------------------------------------------------------------------- /tests/errors/0_errors.test.ts: -------------------------------------------------------------------------------- 1 | import { z } from 'zod' 2 | 3 | import request from 'supertest' 4 | import express from 'express' 5 | import { Handler } from '../../src/index.js' 6 | import { HTTPError, HTTPStatus } from '../../src/utils/error.js' 7 | 8 | const app = express() 9 | 10 | const resolverErrorHandler = new Handler() 11 | .validate('params', { userId: z.string() }) 12 | .resolve(async (req, res, context) => { 13 | throw new HTTPError(HTTPStatus.BAD_REQUEST, 'This should fail in resolver') 14 | }) 15 | .transform((data) => { 16 | return { 17 | data: { 18 | name: 'Hello World', 19 | }, 20 | meta: {}, 21 | } 22 | }) 23 | .express() 24 | 25 | const transformerErrorHandler = new Handler() 26 | .validate('params', { userId: z.string() }) 27 | .resolve(async (req, res, context) => { 28 | return 'ok' 29 | }) 30 | .transform((data) => { 31 | throw new HTTPError(HTTPStatus.BAD_REQUEST, 'This should fail in transformer') 32 | return { 33 | data: { 34 | name: 'Hello World', 35 | }, 36 | meta: {}, 37 | } 38 | }) 39 | .express() 40 | 41 | app.get('/resolver/:userId', resolverErrorHandler) 42 | app.get('/transformer/:userId', transformerErrorHandler) 43 | 44 | // ALWAYS APPEND ERROR HANDLER AFTER ROUTES 45 | app.use((err: any, req: any, res: any, next: any) => { 46 | res.status(500).send('Something broke!') 47 | }) 48 | 49 | // TESTS 50 | 51 | describe('Error Handling', () => { 52 | it('should return error as expected for Resolver', (done) => { 53 | request(app) 54 | .get('/resolver/1') 55 | .expect(400) 56 | .then((res) => { 57 | expect(res.body).toEqual({ 58 | errors: [ 59 | { 60 | message: 'This should fail in resolver', 61 | }, 62 | ], 63 | type: 'Bad Request', 64 | }) 65 | 66 | done() 67 | }) 68 | .catch(done) 69 | }) 70 | 71 | it('should return error as expected for Transformer', (done) => { 72 | request(app) 73 | .get('/transformer/1') 74 | .expect(400) 75 | .then((res) => { 76 | expect(res.body).toEqual({ 77 | errors: [ 78 | { 79 | message: 'This should fail in transformer', 80 | }, 81 | ], 82 | type: 'Bad Request', 83 | }) 84 | 85 | done() 86 | }) 87 | .catch(done) 88 | }) 89 | }) 90 | -------------------------------------------------------------------------------- /tests/middlewares/0_simple-middleware.test.ts: -------------------------------------------------------------------------------- 1 | import { z } from 'zod' 2 | 3 | import request from 'supertest' 4 | import express from 'express' 5 | import { Handler } from '../../src/index.js' 6 | import { HTTPError } from '../../src/utils/error.js' 7 | 8 | const app = express() 9 | 10 | const simpleMiddlewareHandler = new Handler() 11 | .validate('params', { userId: z.string() }) 12 | .validate('query', { fail: z.string() }) 13 | .middleware(async (req) => { 14 | if (req.query.fail === 'yes') { 15 | throw new HTTPError(400, 'This should fail') 16 | } 17 | 18 | if (req.query.fail === 'unknown') { 19 | throw new Error('This should fail') 20 | } 21 | 22 | return { 23 | name: 'John Doe', 24 | } 25 | }) 26 | .resolve(async (req, res, context) => { 27 | return { name: context.name } 28 | }) 29 | .transform((data) => { 30 | return { 31 | data: { 32 | name: data.name, 33 | }, 34 | meta: {}, 35 | } 36 | }) 37 | .express() 38 | 39 | app.get('/user/:userId', simpleMiddlewareHandler) 40 | 41 | // ALWAYS APPEND ERROR HANDLER AFTER ROUTES 42 | app.use((err: any, req: any, res: any, next: any) => { 43 | if (err instanceof HTTPError) { 44 | return res.status(err.status).send(err.message) 45 | } 46 | 47 | res.status(500).send('Something broke!') 48 | }) 49 | 50 | // TESTS 51 | 52 | describe('Middleware Tests', () => { 53 | describe('Single Middleware', () => { 54 | it('should return 200 on success', (done) => { 55 | request(app) 56 | .get('/user/1?fail=no') 57 | .expect(200) 58 | .then((res) => { 59 | expect(res.body).toEqual({ 60 | data: { 61 | name: 'John Doe', 62 | }, 63 | meta: {}, 64 | }) 65 | 66 | done() 67 | }) 68 | .catch(done) 69 | }) 70 | 71 | it('should return 400 on fail', (done) => { 72 | request(app).get('/user/1?fail=yes').expect(400).end(done) 73 | }) 74 | 75 | it('should return 500 on non-HttpError fail', (done) => { 76 | request(app).get('/user/1?fail=unknown').expect(500).end(done) 77 | }) 78 | }) 79 | }) 80 | -------------------------------------------------------------------------------- /tests/middlewares/1_chained-middleware.test.ts: -------------------------------------------------------------------------------- 1 | import { z } from 'zod' 2 | 3 | import request from 'supertest' 4 | import express, { Request } from 'express' 5 | import { Handler } from '../../src/index.js' 6 | import { HTTPError } from '../../src/utils/error.js' 7 | 8 | const app = express() 9 | 10 | const mergedMiddlewareHandler = new Handler() 11 | .validate('params', { userId: z.string() }) 12 | .validate('query', { 13 | failInMiddlewareOne: z.string().optional(), 14 | failInMiddlewareTwo: z.string().optional(), 15 | }) 16 | .middleware(async (req) => { 17 | if (req.query.failInMiddlewareOne === 'yes') { 18 | throw new HTTPError(400, 'This should fail') 19 | } 20 | 21 | return { 22 | name: 'John Doe', 23 | age: 12, 24 | } 25 | }) 26 | .middleware((req) => { 27 | // no return value 28 | }) 29 | .middleware(async (req, res, next, context) => { 30 | if (req.query.failInMiddlewareTwo === 'yes') { 31 | throw new HTTPError(401, 'This should fail in middleware two') 32 | } 33 | 34 | if (context.name !== 'John Doe') { 35 | throw new Error('Middleware two should have the previous context applied') 36 | } 37 | 38 | return { 39 | age: 20, 40 | } 41 | }) 42 | .resolve(async (req, res, context) => { 43 | return { name: context.name, age: context.age } 44 | }) 45 | .transform((data) => { 46 | return { 47 | data: { 48 | name: data.name, 49 | age: data.age, 50 | }, 51 | meta: {}, 52 | } 53 | }) 54 | .express() 55 | 56 | app.get('/user/:userId', mergedMiddlewareHandler) 57 | 58 | // ALWAYS APPEND ERROR HANDLER AFTER ROUTES 59 | app.use((err: any, req: any, res: any, next: any) => { 60 | if (err instanceof HTTPError) { 61 | return res.status(err.status).send(err.message) 62 | } 63 | 64 | res.status(500).send('Something broke!') 65 | }) 66 | 67 | // TESTS 68 | 69 | describe('Chained Middlewares', () => { 70 | it('should return 200 on success', (done) => { 71 | request(app) 72 | .get('/user/1') 73 | .expect(200) 74 | .then((res) => { 75 | expect(res.body).toEqual({ 76 | data: { 77 | name: 'John Doe', 78 | age: 20, 79 | }, 80 | meta: {}, 81 | }) 82 | done() 83 | }) 84 | .catch(done) 85 | }) 86 | 87 | it('should return 400 on fail in first middleware', (done) => { 88 | request(app).get('/user/1?failInMiddlewareOne=yes').expect(400).end(done) 89 | }) 90 | 91 | it('should return 400 on fail in second middleware and not execute second middleware', (done) => { 92 | request(app) 93 | .get('/user/1?failInMiddlewareOne=yes&failInMiddlewareTwo=yes') 94 | .expect(400) 95 | .end(done) 96 | }) 97 | 98 | it('should return 401 on fail in second middleware', (done) => { 99 | request(app).get('/user/1?failInMiddlewareTwo=yes').expect(401).end(done) 100 | }) 101 | }) 102 | -------------------------------------------------------------------------------- /tests/misc/0_no-transformer.test.ts: -------------------------------------------------------------------------------- 1 | import { z } from "zod" 2 | 3 | import request from "supertest" 4 | import express from "express" 5 | import { Handler } from "../../src/index.js" 6 | import { HTTPError } from "../../src/utils/error.js" 7 | 8 | const app = express() 9 | // JSON parser middleware is required for body validation! 10 | app.use(express.json()) 11 | 12 | const miscBaseTest = new Handler() 13 | .resolve(async (req, res) => { 14 | res.status(204).send() 15 | }) 16 | .express() 17 | 18 | const redirectBaseTest = new Handler() 19 | .resolve(async (req, res) => { 20 | res.redirect(301, "/") 21 | }) 22 | .express() 23 | 24 | app.post("/misc/1", miscBaseTest) 25 | app.post("/misc/2", redirectBaseTest) 26 | 27 | // ALWAYS APPEND ERROR HANDLER AFTER ROUTES 28 | app.use((err: any, req: any, res: any, next: any) => { 29 | if (err instanceof HTTPError) { 30 | return res.status(err.status).send(err.message) 31 | } 32 | 33 | res.status(500).send("Something broke!") 34 | }) 35 | 36 | // TESTS 37 | describe("Misc Tests", () => { 38 | it("should return a custom status code without transformer", (done) => { 39 | request(app).post(`/misc/1`).expect(204).end(done) 40 | }) 41 | 42 | it("should redirect with correct code", (done) => { 43 | request(app).post(`/misc/2`).expect(301).expect("Location", "/").end(done) 44 | }) 45 | }) 46 | -------------------------------------------------------------------------------- /tests/validation/0_body-validation.test.ts: -------------------------------------------------------------------------------- 1 | import { z } from 'zod' 2 | 3 | import request from 'supertest' 4 | import express from 'express' 5 | import { Handler } from '../../src/index.js' 6 | import { HTTPError } from '../../src/utils/error.js' 7 | 8 | const app = express() 9 | // JSON parser middleware is required for body validation! 10 | app.use(express.json()) 11 | 12 | const bodyValidationBaseTest = new Handler() 13 | .validate('body', { 14 | user: z.object({ 15 | username: z.string(), 16 | password: z.string(), 17 | }), 18 | }) 19 | .resolve(async (req) => { 20 | return { name: req.body.user.username, password: req.body.user.password } 21 | }) 22 | .transform((data) => { 23 | return { 24 | data: { 25 | name: data.name, 26 | password: data.password, 27 | }, 28 | meta: {}, 29 | } 30 | }) 31 | .express() 32 | 33 | const validateBody = z.object({ 34 | user: z.object({ 35 | username: z.string(), 36 | password: z.string(), 37 | }), 38 | }) 39 | 40 | const bodyValidationExternalValidatorTest = new Handler() 41 | .validate('body', validateBody) 42 | .resolve(async (req) => { 43 | return { name: req.body.user.username, password: req.body.user.password } 44 | }) 45 | .transform((data) => { 46 | return { 47 | data: { 48 | name: data.name, 49 | password: data.password, 50 | }, 51 | meta: {}, 52 | } 53 | }) 54 | .express() 55 | 56 | app.post('/validation/1', bodyValidationBaseTest) 57 | app.post('/validation/2', bodyValidationExternalValidatorTest) 58 | 59 | // ALWAYS APPEND ERROR HANDLER AFTER ROUTES 60 | app.use((err: any, req: any, res: any, next: any) => { 61 | if (err instanceof HTTPError) { 62 | return res.status(err.status).send(err.message) 63 | } 64 | 65 | res.status(500).send('Something broke!') 66 | }) 67 | 68 | // TESTS 69 | describe('Validation Tests', () => { 70 | describe('Basic Body Validaton', () => { 71 | it('should return 200 on success', (done) => { 72 | const user = { 73 | username: 'test1', 74 | password: 'test2', 75 | } 76 | request(app) 77 | .post('/validation/1') 78 | .send({ 79 | user, 80 | }) 81 | .set('Content-Type', 'application/json') 82 | .set('Accept', 'application/json') 83 | .expect(200) 84 | .then((res) => { 85 | expect(res.body).toEqual({ 86 | data: { 87 | name: user.username, 88 | password: user.password, 89 | }, 90 | meta: {}, 91 | }) 92 | 93 | done() 94 | }) 95 | .catch(done) 96 | }) 97 | 98 | it('should return 400 on no body passed', (done) => { 99 | request(app).post('/validation/1').expect(400).end(done) 100 | }) 101 | 102 | it('should return 400 on missing username', (done) => { 103 | const user = { 104 | password: 'test2', 105 | } 106 | request(app) 107 | .post('/validation/1') 108 | .send({ 109 | user, 110 | }) 111 | .set('Content-Type', 'application/json') 112 | .set('Accept', 'application/json') 113 | .expect(400) 114 | .end(done) 115 | }) 116 | 117 | it('should return 400 on wrongly formatted data', (done) => { 118 | request(app) 119 | .post('/validation/1') 120 | .send('string') 121 | .set('Content-Type', 'text/plain') 122 | .expect(400) 123 | .end(done) 124 | 125 | request(app) 126 | .post('/validation/1') 127 | .send(undefined) 128 | .set('Content-Type', 'unset') 129 | .expect(400) 130 | .end(done) 131 | }) 132 | }) 133 | 134 | describe('External Validator Object Body Validaton', () => { 135 | it('should return 200 on success', (done) => { 136 | const user = { 137 | username: 'test1', 138 | password: 'test2', 139 | } 140 | request(app) 141 | .post('/validation/2') 142 | .send({ 143 | user, 144 | }) 145 | .set('Content-Type', 'application/json') 146 | .set('Accept', 'application/json') 147 | .expect(200) 148 | .then((res) => { 149 | expect(res.body).toEqual({ 150 | data: { 151 | name: user.username, 152 | password: user.password, 153 | }, 154 | meta: {}, 155 | }) 156 | 157 | done() 158 | }) 159 | .catch(done) 160 | }) 161 | 162 | it('should return 400 on no body passed', (done) => { 163 | request(app).post('/validation/2').expect(400).end(done) 164 | }) 165 | 166 | it('should return 400 on missing username', (done) => { 167 | const user = { 168 | password: 'test2', 169 | } 170 | request(app) 171 | .post('/validation/2') 172 | .send({ 173 | user, 174 | }) 175 | .set('Content-Type', 'application/json') 176 | .set('Accept', 'application/json') 177 | .expect(400) 178 | .end(done) 179 | }) 180 | 181 | it('should return 400 on wrongly formatted data', (done) => { 182 | request(app) 183 | .post('/validation/2') 184 | .send('string') 185 | .set('Content-Type', 'text/plain') 186 | .expect(400) 187 | .end(done) 188 | 189 | request(app) 190 | .post('/validation/2') 191 | .send(undefined) 192 | .set('Content-Type', 'unset') 193 | .expect(400) 194 | .end(done) 195 | }) 196 | }) 197 | }) 198 | -------------------------------------------------------------------------------- /tests/validation/1_header-validation.test.ts: -------------------------------------------------------------------------------- 1 | import { z } from 'zod' 2 | 3 | import request from 'supertest' 4 | import express from 'express' 5 | import { Handler } from '../../src/index.js' 6 | import { HTTPError } from '../../src/utils/error.js' 7 | 8 | const app = express() 9 | // JSON parser middleware is required for body validation! 10 | app.use(express.json()) 11 | 12 | const headersValidationBaseTest = new Handler() 13 | .validate('headers', { 14 | // MUST be lowercase 15 | authorization: z.string(), 16 | }) 17 | .resolve(async (req) => { 18 | return { auth: req.headers.authorization } 19 | }) 20 | .transform((data) => { 21 | return { 22 | data: { 23 | auth: data.auth, 24 | }, 25 | meta: {}, 26 | } 27 | }) 28 | .express() 29 | 30 | app.post('/validation/1', headersValidationBaseTest) 31 | 32 | // ALWAYS APPEND ERROR HANDLER AFTER ROUTES 33 | app.use((err: any, req: any, res: any, next: any) => { 34 | if (err instanceof HTTPError) { 35 | return res.status(err.status).send(err.message) 36 | } 37 | 38 | res.status(500).send('Something broke!') 39 | }) 40 | 41 | // TESTS 42 | describe('Validation Tests', () => { 43 | describe('Basic Headers Validaton', () => { 44 | it('should return 200 on success', (done) => { 45 | const auth = 'test1' 46 | request(app) 47 | .post('/validation/1') 48 | .set({ Authorization: auth }) 49 | .expect(200) 50 | .then((res) => { 51 | expect(res.body).toEqual({ 52 | data: { 53 | auth, 54 | }, 55 | meta: {}, 56 | }) 57 | 58 | done() 59 | }) 60 | .catch(done) 61 | }) 62 | 63 | it('should return 400 on no headers passed', (done) => { 64 | request(app).post('/validation/1').expect(400).end(done) 65 | }) 66 | 67 | it('should return 400 on wrong headers passed', (done) => { 68 | request(app).post('/validation/1').set({ OtherHeader: '1' }).expect(400).end(done) 69 | }) 70 | }) 71 | }) 72 | -------------------------------------------------------------------------------- /tests/validation/2_params-validation.test.ts: -------------------------------------------------------------------------------- 1 | import { z } from 'zod' 2 | 3 | import request from 'supertest' 4 | import express from 'express' 5 | import { Handler } from '../../src/index.js' 6 | import { HTTPError } from '../../src/utils/error.js' 7 | 8 | const app = express() 9 | // JSON parser middleware is required for body validation! 10 | app.use(express.json()) 11 | 12 | const globalData = { 13 | username: 'test1', 14 | } 15 | 16 | const paramsValidationBaseTest = new Handler() 17 | .validate('params', { 18 | Username: z.enum([globalData.username]), 19 | organization: z.string(), 20 | }) 21 | .resolve(async (req) => { 22 | return { username: req.params.Username, organization: req.params.organization } 23 | }) 24 | .transform((data) => { 25 | return { 26 | data: { 27 | username: data.username, 28 | organization: data.organization, 29 | }, 30 | meta: {}, 31 | } 32 | }) 33 | .express() 34 | 35 | app.post('/validation/:organization/:Username', paramsValidationBaseTest) 36 | 37 | // ALWAYS APPEND ERROR HANDLER AFTER ROUTES 38 | app.use((err: any, req: any, res: any, next: any) => { 39 | if (err instanceof HTTPError) { 40 | return res.status(err.status).send(err.message) 41 | } 42 | 43 | res.status(500).send('Something broke!') 44 | }) 45 | 46 | // TESTS 47 | describe('Validation Tests', () => { 48 | describe('Params Base Validaton', () => { 49 | it('should return 200 on success', (done) => { 50 | const params = { 51 | Username: globalData.username, 52 | organization: 'test2', 53 | } 54 | request(app) 55 | .post(`/validation/${params.organization}/${params.Username}`) 56 | .expect(200) 57 | .then((res) => { 58 | expect(res.body).toEqual({ 59 | data: { 60 | username: params.Username, 61 | organization: params.organization, 62 | }, 63 | meta: {}, 64 | }) 65 | 66 | done() 67 | }) 68 | .catch(done) 69 | }) 70 | 71 | it('should return 400 on wrong params passed', (done) => { 72 | const wrongParams = { 73 | Username: 1, 74 | organization: 2, 75 | } 76 | 77 | request(app) 78 | .post(`/validation/${wrongParams.organization}/${wrongParams.Username}`) 79 | .expect(400) 80 | .end(done) 81 | }) 82 | }) 83 | }) 84 | -------------------------------------------------------------------------------- /tests/validation/3_query-validation.test.ts: -------------------------------------------------------------------------------- 1 | import { z } from 'zod' 2 | 3 | import request from 'supertest' 4 | import express from 'express' 5 | import { Handler } from '../../src/index.js' 6 | import { HTTPError } from '../../src/utils/error.js' 7 | 8 | const app = express() 9 | // JSON parser middleware is required for body validation! 10 | app.use(express.json()) 11 | 12 | const globalData = { 13 | username: 'test1', 14 | } 15 | 16 | const paramsValidationBaseTest = new Handler() 17 | .validate('query', { 18 | Username: z.enum([globalData.username]), 19 | organization: z.string(), 20 | }) 21 | .resolve(async (req) => { 22 | return { username: req.query.Username, organization: req.query.organization } 23 | }) 24 | .transform((data) => { 25 | return { 26 | data: { 27 | username: data.username, 28 | organization: data.organization, 29 | }, 30 | meta: {}, 31 | } 32 | }) 33 | .express() 34 | 35 | app.post('/validation/1', paramsValidationBaseTest) 36 | 37 | // ALWAYS APPEND ERROR HANDLER AFTER ROUTES 38 | app.use((err: any, req: any, res: any, next: any) => { 39 | if (err instanceof HTTPError) { 40 | return res.status(err.status).send(err.message) 41 | } 42 | 43 | res.status(500).send('Something broke!') 44 | }) 45 | 46 | // TESTS 47 | describe('Validation Tests', () => { 48 | describe('Query Base Validaton', () => { 49 | it('should return 200 on success', (done) => { 50 | const queryParams = { 51 | Username: globalData.username, 52 | organization: 'test2', 53 | } 54 | request(app) 55 | .post( 56 | `/validation/1?organization=${queryParams.organization}&Username=${queryParams.Username}` 57 | ) 58 | .expect(200) 59 | .then((res) => { 60 | expect(res.body).toEqual({ 61 | data: { 62 | username: queryParams.Username, 63 | organization: queryParams.organization, 64 | }, 65 | meta: {}, 66 | }) 67 | 68 | done() 69 | }) 70 | .catch(done) 71 | }) 72 | 73 | it('should return 400 on wrong params passed', (done) => { 74 | const wrongParams = { 75 | Username: 1, 76 | organization: 2, 77 | } 78 | 79 | request(app) 80 | .post( 81 | `/validation/1?organization=${wrongParams.organization}&Username=${wrongParams.Username}` 82 | ) 83 | .expect(400) 84 | .end(done) 85 | }) 86 | 87 | it('should return 400 on missing params passed', (done) => { 88 | request(app).post(`/validation/1`).expect(400).end(done) 89 | }) 90 | }) 91 | }) 92 | -------------------------------------------------------------------------------- /tests/validation/4_combined-validation.test.ts: -------------------------------------------------------------------------------- 1 | import { z } from 'zod' 2 | 3 | import request from 'supertest' 4 | import express from 'express' 5 | import { Handler } from '../../src/index.js' 6 | import { HTTPError } from '../../src/utils/error.js' 7 | 8 | const globalData = { 9 | username: 'test1', 10 | password: 'test2', 11 | organization: 'test3', 12 | filters: 'test4', 13 | authorization: 'test5', 14 | } 15 | 16 | const app = express() 17 | // JSON parser middleware is required for body validation! 18 | app.use(express.json()) 19 | 20 | const bodyValidationBaseTest = new Handler() 21 | .validate('body', { 22 | user: z.object({ 23 | username: z.string(), 24 | password: z.string(), 25 | }), 26 | }) 27 | .validate('params', { 28 | organization: z.enum([globalData.organization]), 29 | }) 30 | .validate('query', { 31 | filters: z.string(), 32 | }) 33 | .validate('headers', { 34 | authorization: z.string(), 35 | }) 36 | .middleware(async (req, res, next) => { 37 | if (req.body.user.username === 'FAIL') { 38 | throw new HTTPError(401, 'Username cannot be FAIL') 39 | } 40 | 41 | next() 42 | }) 43 | .resolve(async (req) => { 44 | return { 45 | name: req.body.user.username, 46 | password: req.body.user.password, 47 | organization: req.params.organization, 48 | filters: req.query.filters, 49 | authorization: req.headers.authorization, 50 | } 51 | }) 52 | .transform((data) => { 53 | return { 54 | data: { 55 | name: data.name, 56 | password: data.password, 57 | organization: data.organization, 58 | filters: data.filters, 59 | authorization: data.authorization, 60 | }, 61 | meta: {}, 62 | } 63 | }) 64 | .express() 65 | 66 | const validateBody = z.object({ 67 | user: z.object({ 68 | username: z.string(), 69 | password: z.string(), 70 | }), 71 | }) 72 | 73 | app.post('/validation/:organization', bodyValidationBaseTest) 74 | 75 | // ALWAYS APPEND ERROR HANDLER AFTER ROUTES 76 | app.use((err: any, req: any, res: any, next: any) => { 77 | if (err instanceof HTTPError) { 78 | return res.status(err.status).send(err.message) 79 | } 80 | 81 | res.status(500).send('Something broke!') 82 | }) 83 | 84 | // TESTS 85 | describe('Validation Tests', () => { 86 | describe('Combined Validations', () => { 87 | it('should return 200 on success', (done) => { 88 | request(app) 89 | .post('/validation/' + globalData.organization + '?filters=' + globalData.filters) 90 | .send({ 91 | user: { 92 | username: globalData.username, 93 | password: globalData.password, 94 | }, 95 | }) 96 | .set('Content-Type', 'application/json') 97 | .set('Accept', 'application/json') 98 | .set({ Authorization: globalData.authorization }) 99 | .expect(200) 100 | .then((res) => { 101 | expect(res.body).toEqual({ 102 | data: { 103 | name: globalData.username, 104 | password: globalData.password, 105 | organization: globalData.organization, 106 | filters: globalData.filters, 107 | authorization: globalData.authorization, 108 | }, 109 | meta: {}, 110 | }) 111 | 112 | done() 113 | }) 114 | .catch(done) 115 | }) 116 | 117 | it('should return 400 on invalid body', (done) => { 118 | request(app) 119 | .post('/validation/' + globalData.organization) 120 | .expect(400) 121 | .end(done) 122 | }) 123 | 124 | it('should return 400 on invalid params', (done) => { 125 | request(app) 126 | .post('/validation/WRONGVALUE' + '?filters=' + globalData.filters) 127 | .send({ 128 | user: { 129 | username: globalData.username, 130 | password: globalData.password, 131 | }, 132 | }) 133 | .set('Content-Type', 'application/json') 134 | .set('Accept', 'application/json') 135 | .set({ Authorization: globalData.authorization }) 136 | .expect(400) 137 | .end(done) 138 | }) 139 | 140 | it('should return 400 on invalid query', (done) => { 141 | request(app) 142 | .post('/validation/' + globalData.organization + '?otherQueryThanFilters=FAIL') 143 | .send({ 144 | user: { 145 | username: globalData.username, 146 | password: globalData.password, 147 | }, 148 | }) 149 | .set('Content-Type', 'application/json') 150 | .set('Accept', 'application/json') 151 | .set({ Authorization: globalData.authorization }) 152 | .expect(400) 153 | .end(done) 154 | }) 155 | 156 | it('should return 400 on invalid headers', (done) => { 157 | request(app) 158 | .post('/validation/' + globalData.organization + '?filters=' + globalData.filters) 159 | .send({ 160 | user: { 161 | username: globalData.username, 162 | password: globalData.password, 163 | }, 164 | }) 165 | .set('Content-Type', 'application/json') 166 | .set('Accept', 'application/json') 167 | .set({ NotProvided: 'FAIL' }) 168 | .expect(400) 169 | .end(done) 170 | }) 171 | 172 | it('should return 400 on middleware validation fail', (done) => { 173 | request(app) 174 | .post('/validation/' + globalData.organization + '?filters=' + globalData.filters) 175 | .send({ 176 | user: { 177 | username: 'FAIL', 178 | password: globalData.password, 179 | }, 180 | }) 181 | .set('Content-Type', 'application/json') 182 | .set('Accept', 'application/json') 183 | .set({ Authorization: globalData.authorization }) 184 | .expect(401) 185 | .end(done) 186 | }) 187 | }) 188 | }) 189 | -------------------------------------------------------------------------------- /tests/validation/5_validation_transforms.test.ts: -------------------------------------------------------------------------------- 1 | import { z } from 'zod' 2 | 3 | import request from 'supertest' 4 | import express from 'express' 5 | import { Handler } from '../../src/index.js' 6 | import { HTTPError } from '../../src/utils/error.js' 7 | 8 | const globalData = { 9 | username: 'test1', 10 | password: 'test2', 11 | organization: 'test3', 12 | filters: 'test4', 13 | authorization: 'test5', 14 | } 15 | 16 | const app = express() 17 | // JSON parser middleware is required for body validation! 18 | app.use(express.json()) 19 | 20 | const bodyValidationBaseTest = new Handler() 21 | .validate('body', { 22 | user: z 23 | .object({ 24 | username: z.string(), 25 | password: z.string(), 26 | }) 27 | .transform((data) => { 28 | return { 29 | // flip it 30 | username: data.password, 31 | password: data.username, 32 | } 33 | }), 34 | }) 35 | .validate('params', { 36 | organization: z.enum([globalData.organization]), 37 | test: z.string().refine((data) => data === 'test', { 38 | message: 'Test must be test', 39 | }), 40 | }) 41 | .validate('query', { 42 | filters: z.string().transform(async (data) => { 43 | // wait for 1 second 44 | await new Promise((resolve) => setTimeout(resolve, 1000)) 45 | 46 | return data.toUpperCase() 47 | }), 48 | }) 49 | .validate('headers', { 50 | authorization: z.string().transform((data) => { 51 | return data.toLowerCase() 52 | }), 53 | }) 54 | .middleware(async (req, res, next) => { 55 | if (req.body.user.username === 'FAIL') { 56 | throw new HTTPError(401, 'Username cannot be FAIL') 57 | } 58 | 59 | next() 60 | }) 61 | .resolve(async (req) => { 62 | const oldPassword = req.body.user.password 63 | const oldUsername = req.body.user.username 64 | 65 | req.body.user.username = oldPassword 66 | req.body.user.password = oldUsername 67 | 68 | return { 69 | name: req.body.user.username, 70 | password: req.body.user.password, 71 | organization: req.params.organization, 72 | filters: req.query.filters, 73 | authorization: req.headers.authorization, 74 | } 75 | }) 76 | .transform((data) => { 77 | return { 78 | data: { 79 | name: data.name, 80 | password: data.password, 81 | organization: data.organization, 82 | filters: data.filters, 83 | authorization: data.authorization, 84 | }, 85 | meta: {}, 86 | } 87 | }) 88 | .express() 89 | 90 | const validateBody = z.object({ 91 | user: z.object({ 92 | username: z.string(), 93 | password: z.string(), 94 | }), 95 | }) 96 | 97 | app.post('/validation/:organization/:test', bodyValidationBaseTest) 98 | 99 | // ALWAYS APPEND ERROR HANDLER AFTER ROUTES 100 | app.use((err: any, req: any, res: any, next: any) => { 101 | if (err instanceof HTTPError) { 102 | return res.status(err.status).send(err.message) 103 | } 104 | 105 | res.status(500).send('Something broke!') 106 | }) 107 | 108 | // TESTS 109 | describe('Validation Tests', () => { 110 | describe('Transform validations', () => { 111 | it('should return 200 on success', (done) => { 112 | request(app) 113 | .post( 114 | '/validation/' + 115 | globalData.organization + 116 | '/test' + 117 | '?filters=' + 118 | globalData.filters 119 | ) 120 | .send({ 121 | user: { 122 | username: globalData.username, 123 | password: globalData.password, 124 | }, 125 | }) 126 | .set('Content-Type', 'application/json') 127 | .set('Accept', 'application/json') 128 | .set({ Authorization: globalData.authorization }) 129 | .expect(200) 130 | .then((res) => { 131 | expect(res.body).toEqual({ 132 | data: { 133 | name: globalData.username, 134 | password: globalData.password, 135 | organization: globalData.organization, 136 | filters: globalData.filters.toUpperCase(), 137 | authorization: globalData.authorization, 138 | }, 139 | meta: {}, 140 | }) 141 | 142 | done() 143 | }) 144 | .catch(done) 145 | }) 146 | 147 | it('should return 400 on invalid body', (done) => { 148 | request(app) 149 | .post( 150 | '/validation/' + 151 | globalData.organization + 152 | '/test' + 153 | '?filters=' + 154 | globalData.filters 155 | ) 156 | .expect(400) 157 | .end(done) 158 | }) 159 | 160 | it('should return 400 on invalid params (refined)', (done) => { 161 | request(app) 162 | .post( 163 | '/validation/' + 164 | globalData.organization + 165 | '/not-test' + 166 | '?filters=' + 167 | globalData.filters 168 | ) 169 | .send({ 170 | user: { 171 | username: globalData.username, 172 | password: globalData.password, 173 | }, 174 | }) 175 | .set('Content-Type', 'application/json') 176 | .set('Accept', 'application/json') 177 | .set({ Authorization: globalData.authorization }) 178 | .expect(400) 179 | .end(done) 180 | }) 181 | 182 | it('should return 400 on invalid params', (done) => { 183 | request(app) 184 | .post('/validation/WRONGVALUE/test' + '?filters=' + globalData.filters) 185 | .send({ 186 | user: { 187 | username: globalData.username, 188 | password: globalData.password, 189 | }, 190 | }) 191 | .set('Content-Type', 'application/json') 192 | .set('Accept', 'application/json') 193 | .set({ Authorization: globalData.authorization }) 194 | .expect(400) 195 | .end(done) 196 | }) 197 | 198 | it('should return 400 on invalid query', (done) => { 199 | request(app) 200 | .post('/validation/' + globalData.organization + '/test?otherQueryThanFilters=FAIL') 201 | .send({ 202 | user: { 203 | username: globalData.username, 204 | password: globalData.password, 205 | }, 206 | }) 207 | .set('Content-Type', 'application/json') 208 | .set('Accept', 'application/json') 209 | .set({ Authorization: globalData.authorization }) 210 | .expect(400) 211 | .end(done) 212 | }) 213 | 214 | it('should return 400 on invalid headers', (done) => { 215 | request(app) 216 | .post( 217 | '/validation/' + globalData.organization + '/test?filters=' + globalData.filters 218 | ) 219 | .send({ 220 | user: { 221 | username: globalData.username, 222 | password: globalData.password, 223 | }, 224 | }) 225 | .set('Content-Type', 'application/json') 226 | .set('Accept', 'application/json') 227 | .set({ NotProvided: 'FAIL' }) 228 | .expect(400) 229 | .end(done) 230 | }) 231 | 232 | it('should return 400 on middleware validation fail', (done) => { 233 | request(app) 234 | .post( 235 | '/validation/' + globalData.organization + '/test?filters=' + globalData.filters 236 | ) 237 | .send({ 238 | user: { 239 | username: globalData.password, 240 | password: 'FAIL', 241 | }, 242 | }) 243 | .set('Content-Type', 'application/json') 244 | .set('Accept', 'application/json') 245 | .set({ Authorization: globalData.authorization }) 246 | .expect(401) 247 | .end(done) 248 | }) 249 | }) 250 | }) 251 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "esModuleInterop": true, 4 | "skipLibCheck": true, 5 | "target": "es2022", 6 | "allowJs": true, 7 | "resolveJsonModule": true, 8 | "moduleDetection": "force", 9 | "experimentalDecorators": true, 10 | "strict": true, 11 | "noUncheckedIndexedAccess": true, 12 | "moduleResolution": "NodeNext", 13 | "module": "NodeNext", 14 | 15 | "outDir": "dist", 16 | "sourceMap": true, 17 | 18 | "lib": ["es2022"], 19 | "composite": true, 20 | "declarationMap": true, 21 | "declaration": true, 22 | "rootDir": "src", 23 | }, 24 | "include": ["src/**/*"], 25 | } 26 | --------------------------------------------------------------------------------