@ibabkin/openapi-express-server 1.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md ADDED
@@ -0,0 +1,58 @@
1
+ # @ibabkin/openapi-express-server
2
+
3
+ Express.js building blocks for wiring routes from an OpenAPI spec to controllers resolved from a
4
+ [`ts-ioc-container`](https://github.com/IgorBabkin/ts-ioc-container) scope. Pairs with
5
+ `@ibabkin/openapi-to-server` (controller interfaces) and `@ibabkin/openapi-to-zod`
6
+ (Zod payload validators).
7
+
8
+ ## Exports
9
+
10
+ - `extractRoutes(spec)` — flattens an `OpenAPIV3.Document` into `RouteMetadata[]` (path, method, operationId, controller/method names derived from the first tag and `operationId`)
11
+ - `convertOpenAPIPathToExpress(path)` — `/users/{id}` → `/users/:id`
12
+ - `buildPayload(req)` — picks `params`, `query`, `body`, `headers` off an Express request
13
+ - `containerMiddleware(appContainer)` — creates a request-scoped child container, attaches it to `req.container`, and disposes it when the response finishes
14
+ - `getContainerOrFail(req)` — reads `req.container` or throws
15
+
16
+ ## Usage
17
+
18
+ Register controllers in an application container keyed by their OpenAPI tag, then register one Express route per
19
+ operation. A reference `RouteBuilder` that does exactly this lives in [`__tests__/RouteBuilder.ts`](./__tests__/RouteBuilder.ts):
20
+
21
+ ```typescript
22
+ import 'reflect-metadata';
23
+ import express from 'express';
24
+ import { Container, Registration } from 'ts-ioc-container';
25
+ import { containerMiddleware } from '@ibabkin/openapi-express-server';
26
+ import { PAYLOADS } from './generated-validators';
27
+ import { RouteBuilder } from './RouteBuilder';
28
+
29
+ const container = new Container({ tags: ['application'] });
30
+ container.addRegistration(Registration.fromClass(UsersController).bindToKey('Users'));
31
+
32
+ const app = express();
33
+ app.use(express.json());
34
+ app.use(containerMiddleware(container));
35
+
36
+ container.resolve(RouteBuilder, { args: [spec, PAYLOADS] }).applyTo(app);
37
+
38
+ app.use((error, req, res, next) => {
39
+ res.status(error instanceof ZodError ? 400 : 500).json({ error: error.message });
40
+ });
41
+ ```
42
+
43
+ Each request resolves the controller from the request scope, validates `req` with the Zod schema for the
44
+ `operationId`, calls `controller[methodName](payload)`, and writes `{ status, headers, body }` to the response.
45
+
46
+ ## Development
47
+
48
+ ```bash
49
+ pnpm build
50
+ pnpm test
51
+ ```
52
+
53
+ `__tests__/integration/generated.spec.ts` generates types and validators from `__tests__/integration/api.yaml` at
54
+ test time and runs an end-to-end server against them.
55
+
56
+ ## License
57
+
58
+ ISC
@@ -0,0 +1,5 @@
1
+ import { RequestHandler, Request } from 'express-serve-static-core';
2
+ import { IContainer } from 'ts-ioc-container';
3
+ export declare function containerMiddleware(appContainer: IContainer): RequestHandler;
4
+ export declare function getContainerOrFail(req: Request): IContainer;
5
+ //# sourceMappingURL=containerMiddleware.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"containerMiddleware.d.ts","sourceRoot":"","sources":["../lib/containerMiddleware.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,OAAO,EAAE,MAAM,2BAA2B,CAAC;AACpE,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAE9C,wBAAgB,mBAAmB,CAAC,YAAY,EAAE,UAAU,GAAG,cAAc,CAsB5E;AAED,wBAAgB,kBAAkB,CAAC,GAAG,EAAE,OAAO,cAK9C"}
@@ -0,0 +1,26 @@
1
+ export function containerMiddleware(appContainer) {
2
+ return (req, res, next) => {
3
+ // Create request-scoped container
4
+ const requestScope = appContainer.createScope({ tags: ['request'] });
5
+ // Attach to request
6
+ req.container = requestScope;
7
+ // Cleanup on response finish
8
+ res.on('finish', () => {
9
+ requestScope.dispose();
10
+ });
11
+ // Cleanup on error
12
+ res.on('close', () => {
13
+ if (!res.writableEnded) {
14
+ requestScope.dispose();
15
+ }
16
+ });
17
+ next();
18
+ };
19
+ }
20
+ export function getContainerOrFail(req) {
21
+ if (!req.container) {
22
+ throw new Error(`Container is not provided`);
23
+ }
24
+ return req.container;
25
+ }
26
+ //# sourceMappingURL=containerMiddleware.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"containerMiddleware.js","sourceRoot":"","sources":["../lib/containerMiddleware.ts"],"names":[],"mappings":"AAGA,MAAM,UAAU,mBAAmB,CAAC,YAAwB;IAC1D,OAAO,CAAC,GAAG,EAAE,GAAG,EAAE,IAAI,EAAE,EAAE;QACxB,kCAAkC;QAClC,MAAM,YAAY,GAAG,YAAY,CAAC,WAAW,CAAC,EAAE,IAAI,EAAE,CAAC,SAAS,CAAC,EAAE,CAAC,CAAC;QAErE,oBAAoB;QACpB,GAAG,CAAC,SAAS,GAAG,YAAY,CAAC;QAE7B,6BAA6B;QAC7B,GAAG,CAAC,EAAE,CAAC,QAAQ,EAAE,GAAG,EAAE;YACpB,YAAY,CAAC,OAAO,EAAE,CAAC;QACzB,CAAC,CAAC,CAAC;QAEH,mBAAmB;QACnB,GAAG,CAAC,EAAE,CAAC,OAAO,EAAE,GAAG,EAAE;YACnB,IAAI,CAAC,GAAG,CAAC,aAAa,EAAE,CAAC;gBACvB,YAAY,CAAC,OAAO,EAAE,CAAC;YACzB,CAAC;QACH,CAAC,CAAC,CAAC;QAEH,IAAI,EAAE,CAAC;IACT,CAAC,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,kBAAkB,CAAC,GAAY;IAC7C,IAAI,CAAC,GAAG,CAAC,SAAS,EAAE,CAAC;QACnB,MAAM,IAAI,KAAK,CAAC,2BAA2B,CAAC,CAAC;IAC/C,CAAC;IACD,OAAO,GAAG,CAAC,SAAS,CAAC;AACvB,CAAC"}
package/esm/index.d.ts ADDED
@@ -0,0 +1,5 @@
1
+ export { OpenAPIServerConfig, RouteMetadata, ErrorHandler, ControllerInstance } from './types.js';
2
+ export { buildPayload, Payload } from './payloadBuilder.js';
3
+ export { extractRoutes, convertOpenAPIPathToExpress } from './routeExtractor.js';
4
+ export { containerMiddleware, getContainerOrFail } from './containerMiddleware.js';
5
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../lib/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,mBAAmB,EAAE,aAAa,EAAE,YAAY,EAAE,kBAAkB,EAAE,MAAM,YAAY,CAAC;AAClG,OAAO,EAAE,YAAY,EAAE,OAAO,EAAE,MAAM,qBAAqB,CAAC;AAC5D,OAAO,EAAE,aAAa,EAAE,2BAA2B,EAAE,MAAM,qBAAqB,CAAC;AACjF,OAAO,EAAE,mBAAmB,EAAE,kBAAkB,EAAE,MAAM,0BAA0B,CAAC"}
package/esm/index.js ADDED
@@ -0,0 +1,4 @@
1
+ export { buildPayload } from './payloadBuilder.js';
2
+ export { extractRoutes, convertOpenAPIPathToExpress } from './routeExtractor.js';
3
+ export { containerMiddleware, getContainerOrFail } from './containerMiddleware.js';
4
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../lib/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,YAAY,EAAW,MAAM,qBAAqB,CAAC;AAC5D,OAAO,EAAE,aAAa,EAAE,2BAA2B,EAAE,MAAM,qBAAqB,CAAC;AACjF,OAAO,EAAE,mBAAmB,EAAE,kBAAkB,EAAE,MAAM,0BAA0B,CAAC"}
@@ -0,0 +1,9 @@
1
+ import { Request } from 'express';
2
+ export interface Payload {
3
+ params?: Record<string, any>;
4
+ query?: Record<string, any>;
5
+ body?: any;
6
+ headers?: Record<string, any>;
7
+ }
8
+ export declare function buildPayload(req: Request): Payload;
9
+ //# sourceMappingURL=payloadBuilder.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"payloadBuilder.d.ts","sourceRoot":"","sources":["../lib/payloadBuilder.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,SAAS,CAAC;AAElC,MAAM,WAAW,OAAO;IACtB,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;IAC7B,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;IAC5B,IAAI,CAAC,EAAE,GAAG,CAAC;IACX,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;CAC/B;AAED,wBAAgB,YAAY,CAAC,GAAG,EAAE,OAAO,GAAG,OAAO,CAoBlD"}
@@ -0,0 +1,17 @@
1
+ export function buildPayload(req) {
2
+ const payload = {};
3
+ if (req.params && Object.keys(req.params).length > 0) {
4
+ payload.params = req.params;
5
+ }
6
+ if (req.query && Object.keys(req.query).length > 0) {
7
+ payload.query = req.query;
8
+ }
9
+ if (req.body !== undefined && req.body !== null) {
10
+ payload.body = req.body;
11
+ }
12
+ if (req.headers && Object.keys(req.headers).length > 0) {
13
+ payload.headers = req.headers;
14
+ }
15
+ return payload;
16
+ }
17
+ //# sourceMappingURL=payloadBuilder.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"payloadBuilder.js","sourceRoot":"","sources":["../lib/payloadBuilder.ts"],"names":[],"mappings":"AASA,MAAM,UAAU,YAAY,CAAC,GAAY;IACvC,MAAM,OAAO,GAAY,EAAE,CAAC;IAE5B,IAAI,GAAG,CAAC,MAAM,IAAI,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QACrD,OAAO,CAAC,MAAM,GAAG,GAAG,CAAC,MAAM,CAAC;IAC9B,CAAC;IAED,IAAI,GAAG,CAAC,KAAK,IAAI,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QACnD,OAAO,CAAC,KAAK,GAAG,GAAG,CAAC,KAA4B,CAAC;IACnD,CAAC;IAED,IAAI,GAAG,CAAC,IAAI,KAAK,SAAS,IAAI,GAAG,CAAC,IAAI,KAAK,IAAI,EAAE,CAAC;QAChD,OAAO,CAAC,IAAI,GAAG,GAAG,CAAC,IAAI,CAAC;IAC1B,CAAC;IAED,IAAI,GAAG,CAAC,OAAO,IAAI,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QACvD,OAAO,CAAC,OAAO,GAAG,GAAG,CAAC,OAA8B,CAAC;IACvD,CAAC;IAED,OAAO,OAAO,CAAC;AACjB,CAAC"}
@@ -0,0 +1,5 @@
1
+ import { OpenAPIV3 } from 'openapi-types';
2
+ import { RouteMetadata } from './types.js';
3
+ export declare function extractRoutes(spec: OpenAPIV3.Document): RouteMetadata[];
4
+ export declare function convertOpenAPIPathToExpress(openApiPath: string): string;
5
+ //# sourceMappingURL=routeExtractor.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"routeExtractor.d.ts","sourceRoot":"","sources":["../lib/routeExtractor.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,eAAe,CAAC;AAC1C,OAAO,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAE3C,wBAAgB,aAAa,CAAC,IAAI,EAAE,SAAS,CAAC,QAAQ,GAAG,aAAa,EAAE,CA0CvE;AAMD,wBAAgB,2BAA2B,CAAC,WAAW,EAAE,MAAM,GAAG,MAAM,CAEvE"}
@@ -0,0 +1,41 @@
1
+ export function extractRoutes(spec) {
2
+ const routes = [];
3
+ if (!spec.paths) {
4
+ return routes;
5
+ }
6
+ for (const [path, pathItem] of Object.entries(spec.paths)) {
7
+ if (!pathItem)
8
+ continue;
9
+ const methods = ['get', 'post', 'put', 'patch', 'delete', 'options', 'head'];
10
+ for (const method of methods) {
11
+ const operation = pathItem[method];
12
+ if (!operation || !operation.operationId) {
13
+ continue;
14
+ }
15
+ const tags = operation.tags || [];
16
+ const firstTag = tags[0];
17
+ if (!firstTag) {
18
+ console.warn(`Operation ${operation.operationId} has no tags, skipping`);
19
+ continue;
20
+ }
21
+ const controllerName = capitalizeFirst(firstTag);
22
+ const methodName = operation.operationId;
23
+ routes.push({
24
+ path,
25
+ method: method.toUpperCase(),
26
+ operationId: operation.operationId,
27
+ tags,
28
+ controllerName,
29
+ methodName,
30
+ });
31
+ }
32
+ }
33
+ return routes;
34
+ }
35
+ function capitalizeFirst(str) {
36
+ return str.charAt(0).toUpperCase() + str.slice(1);
37
+ }
38
+ export function convertOpenAPIPathToExpress(openApiPath) {
39
+ return openApiPath.replace(/{([^}]+)}/g, ':$1');
40
+ }
41
+ //# sourceMappingURL=routeExtractor.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"routeExtractor.js","sourceRoot":"","sources":["../lib/routeExtractor.ts"],"names":[],"mappings":"AAGA,MAAM,UAAU,aAAa,CAAC,IAAwB;IACpD,MAAM,MAAM,GAAoB,EAAE,CAAC;IAEnC,IAAI,CAAC,IAAI,CAAC,KAAK,EAAE,CAAC;QAChB,OAAO,MAAM,CAAC;IAChB,CAAC;IAED,KAAK,MAAM,CAAC,IAAI,EAAE,QAAQ,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC;QAC1D,IAAI,CAAC,QAAQ;YAAE,SAAS;QAExB,MAAM,OAAO,GAAG,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,EAAE,QAAQ,EAAE,SAAS,EAAE,MAAM,CAAU,CAAC;QAEtF,KAAK,MAAM,MAAM,IAAI,OAAO,EAAE,CAAC;YAC7B,MAAM,SAAS,GAAG,QAAQ,CAAC,MAAM,CAA0C,CAAC;YAE5E,IAAI,CAAC,SAAS,IAAI,CAAC,SAAS,CAAC,WAAW,EAAE,CAAC;gBACzC,SAAS;YACX,CAAC;YAED,MAAM,IAAI,GAAG,SAAS,CAAC,IAAI,IAAI,EAAE,CAAC;YAClC,MAAM,QAAQ,GAAG,IAAI,CAAC,CAAC,CAAC,CAAC;YAEzB,IAAI,CAAC,QAAQ,EAAE,CAAC;gBACd,OAAO,CAAC,IAAI,CAAC,aAAa,SAAS,CAAC,WAAW,wBAAwB,CAAC,CAAC;gBACzE,SAAS;YACX,CAAC;YAED,MAAM,cAAc,GAAG,eAAe,CAAC,QAAQ,CAAC,CAAC;YACjD,MAAM,UAAU,GAAG,SAAS,CAAC,WAAW,CAAC;YAEzC,MAAM,CAAC,IAAI,CAAC;gBACV,IAAI;gBACJ,MAAM,EAAE,MAAM,CAAC,WAAW,EAAE;gBAC5B,WAAW,EAAE,SAAS,CAAC,WAAW;gBAClC,IAAI;gBACJ,cAAc;gBACd,UAAU;aACX,CAAC,CAAC;QACL,CAAC;IACH,CAAC;IAED,OAAO,MAAM,CAAC;AAChB,CAAC;AAED,SAAS,eAAe,CAAC,GAAW;IAClC,OAAO,GAAG,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,WAAW,EAAE,GAAG,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;AACpD,CAAC;AAED,MAAM,UAAU,2BAA2B,CAAC,WAAmB;IAC7D,OAAO,WAAW,CAAC,OAAO,CAAC,YAAY,EAAE,KAAK,CAAC,CAAC;AAClD,CAAC"}
package/esm/types.d.ts ADDED
@@ -0,0 +1,29 @@
1
+ import { Request, Response, NextFunction } from 'express';
2
+ import { OpenAPIV3 } from 'openapi-types';
3
+ import { IContainer } from 'ts-ioc-container';
4
+ export interface OpenAPIServerConfig {
5
+ spec: OpenAPIV3.Document;
6
+ controllers: Record<string, any>;
7
+ basePath?: string;
8
+ errorHandler?: ErrorHandler;
9
+ }
10
+ export interface RouteMetadata {
11
+ path: string;
12
+ method: string;
13
+ operationId: string;
14
+ tags: string[];
15
+ controllerName: string;
16
+ methodName: string;
17
+ }
18
+ export type ErrorHandler = (error: Error, req: Request, res: Response, next: NextFunction) => void;
19
+ export interface ControllerInstance {
20
+ [methodName: string]: (payload: any) => Promise<any>;
21
+ }
22
+ declare global {
23
+ namespace Express {
24
+ interface Request {
25
+ container: IContainer;
26
+ }
27
+ }
28
+ }
29
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../lib/types.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AAC1D,OAAO,EAAE,SAAS,EAAE,MAAM,eAAe,CAAC;AAC1C,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAE9C,MAAM,WAAW,mBAAmB;IAClC,IAAI,EAAE,SAAS,CAAC,QAAQ,CAAC;IACzB,WAAW,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;IACjC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,YAAY,CAAC,EAAE,YAAY,CAAC;CAC7B;AAED,MAAM,WAAW,aAAa;IAC5B,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;IACf,WAAW,EAAE,MAAM,CAAC;IACpB,IAAI,EAAE,MAAM,EAAE,CAAC;IACf,cAAc,EAAE,MAAM,CAAC;IACvB,UAAU,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,MAAM,YAAY,GAAG,CAAC,KAAK,EAAE,KAAK,EAAE,GAAG,EAAE,OAAO,EAAE,GAAG,EAAE,QAAQ,EAAE,IAAI,EAAE,YAAY,KAAK,IAAI,CAAC;AAEnG,MAAM,WAAW,kBAAkB;IACjC,CAAC,UAAU,EAAE,MAAM,GAAG,CAAC,OAAO,EAAE,GAAG,KAAK,OAAO,CAAC,GAAG,CAAC,CAAC;CACtD;AAED,OAAO,CAAC,MAAM,CAAC;IAEb,UAAU,OAAO,CAAC;QAChB,UAAU,OAAO;YACf,SAAS,EAAE,UAAU,CAAC;SACvB;KACF;CACF"}
package/esm/types.js ADDED
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../lib/types.ts"],"names":[],"mappings":""}
package/package.json ADDED
@@ -0,0 +1,73 @@
1
+ {
2
+ "name": "@ibabkin/openapi-express-server",
3
+ "version": "1.1.0",
4
+ "publishConfig": {
5
+ "access": "public",
6
+ "registry": "https://registry.npmjs.org/"
7
+ },
8
+ "description": "Express.js server implementation for OpenAPI-generated interfaces",
9
+ "keywords": [
10
+ "openapi",
11
+ "swagger",
12
+ "express",
13
+ "server",
14
+ "typescript"
15
+ ],
16
+ "author": "ibabkin <igba14@gmail.com>",
17
+ "homepage": "https://github.com/IgorBabkin/openapi-to-server/tree/master/packages/openapi-express-server",
18
+ "license": "ISC",
19
+ "type": "module",
20
+ "main": "esm/index.js",
21
+ "types": "esm/index.d.ts",
22
+ "exports": {
23
+ ".": {
24
+ "import": "./esm/index.js",
25
+ "types": "./esm/index.d.ts"
26
+ }
27
+ },
28
+ "module": "esm/index.js",
29
+ "repository": {
30
+ "type": "git",
31
+ "url": "git+https://github.com/IgorBabkin/openapi-to-server.git"
32
+ },
33
+ "directories": {
34
+ "lib": "lib",
35
+ "test": "__tests__"
36
+ },
37
+ "files": [
38
+ "esm/**/*"
39
+ ],
40
+ "scripts": {
41
+ "build": "pnpm run clean && pnpm run build:ts",
42
+ "test": "jest",
43
+ "test:generate:code": "tsx ./__tests__/generate.ts && prettier --write __tests__/controller-interfaces.ts __tests__/validators.ts",
44
+ "test:watch": "jest --coverage --watch",
45
+ "clean": "rimraf esm *.tsbuildinfo",
46
+ "watch": "nodemon --watch lib --exec 'pnpm run compile'",
47
+ "build:ts": "tsc -p tsconfig.prod.json"
48
+ },
49
+ "dependencies": {
50
+ "@ibabkin/openapi-to-server": "workspace:*",
51
+ "@ibabkin/openapi-to-zod": "workspace:*",
52
+ "express": "^4.18.2",
53
+ "js-yaml": "^4.1.0",
54
+ "reflect-metadata": "^0.2.2",
55
+ "ts-ioc-container": "^46.8.0",
56
+ "zod": "^4.2.1"
57
+ },
58
+ "devDependencies": {
59
+ "@jest/test-sequencer": "29.7.0",
60
+ "@types/express": "^4.17.21",
61
+ "@types/jest": "29.5.14",
62
+ "@types/js-yaml": "^4.0.5",
63
+ "@types/node": "^20.10.0",
64
+ "@types/supertest": "^6.0.2",
65
+ "jest": "29.7.0",
66
+ "openapi-types": "^12.1.0",
67
+ "rimraf": "3.0.2",
68
+ "supertest": "^6.3.3",
69
+ "ts-jest": "29.2.5",
70
+ "tsx": "^4.21.0",
71
+ "typescript": "5.7.3"
72
+ }
73
+ }