snap-on-openapi 1.0.5 → 1.0.7
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/dist/OpenApi.d.ts +2 -0
- package/dist/OpenApi.js +9 -4
- package/dist/services/ExpressWrapper/ExpressWrapper.d.ts +2 -0
- package/dist/services/ExpressWrapper/ExpressWrapper.js +27 -2
- package/dist/services/ExpressWrapper/types/ExpressRequest.d.ts +3 -0
- package/dist/services/RoutingFactory/RoutingFactory.d.ts +1 -1
- package/dist/services/RoutingFactory/RoutingFactory.js +1 -0
- package/dist/services/TanstackStartWrapper/TanstackStartWrapper.js +1 -0
- package/dist/types/AnyRoute.d.ts +1 -1
- package/dist/types/Route.d.ts +10 -2
- package/dist/types/config/Config.d.ts +6 -0
- package/package.json +2 -2
package/dist/OpenApi.d.ts
CHANGED
|
@@ -49,10 +49,12 @@ export declare class OpenApi<TRouteTypes extends string, TErrorCodes extends str
|
|
|
49
49
|
processRootRoute(originalReq: Request): Promise<{
|
|
50
50
|
status: number;
|
|
51
51
|
body: unknown;
|
|
52
|
+
headers: Record<string, string>;
|
|
52
53
|
}>;
|
|
53
54
|
protected handleError(e: unknown, req: Request): {
|
|
54
55
|
status: number;
|
|
55
56
|
body: z.TypeOf<import("./index.js").OpenApiErrorConfigMap<TErrorCodes>[TErrorCodes]["responseValidator"]>;
|
|
57
|
+
headers: {};
|
|
56
58
|
};
|
|
57
59
|
protected static getBuilder(): ConfigBuilder<SampleRouteType, ErrorCode, DefaultErrorMap, DefaultRouteParamsMap, DefaultRouteContextMap, DefaultRouteMap, DefaultConfig>;
|
|
58
60
|
}
|
package/dist/OpenApi.js
CHANGED
|
@@ -235,12 +235,17 @@ export class OpenApi {
|
|
|
235
235
|
}
|
|
236
236
|
});
|
|
237
237
|
}
|
|
238
|
-
const
|
|
238
|
+
const finalResponse = route.validators.responseHeaders ? response : { body: response, headers: {} };
|
|
239
|
+
const finalResponseValidator = z.object({
|
|
240
|
+
body: route.validators.response,
|
|
241
|
+
headers: route.validators.responseHeaders?.strict() ?? z.object({}),
|
|
242
|
+
});
|
|
243
|
+
const validated = finalResponseValidator.safeParse(finalResponse);
|
|
239
244
|
if (!validated.success) {
|
|
240
245
|
throw new ValidationError(validated.error, ValidationLocation.Response);
|
|
241
246
|
}
|
|
242
247
|
this.logger.info('Response: 200', validated.data);
|
|
243
|
-
return { status: 200, body: validated.data };
|
|
248
|
+
return { status: 200, body: validated.data.body, headers: validated.data.headers };
|
|
244
249
|
}
|
|
245
250
|
catch (e) {
|
|
246
251
|
return this.handleError(e, originalReq);
|
|
@@ -256,12 +261,12 @@ export class OpenApi {
|
|
|
256
261
|
throw new Error("Error response haven't passed validation");
|
|
257
262
|
}
|
|
258
263
|
this.logger.info(`Response: '${status}'`, response.body);
|
|
259
|
-
return { status: Number(status), body: response.body };
|
|
264
|
+
return { status: Number(status), body: response.body, headers: {} };
|
|
260
265
|
}
|
|
261
266
|
catch (e) {
|
|
262
267
|
this.logger.error('Error during error handling', e);
|
|
263
268
|
const status = this.config.errors[this.config.defaultError.code].status;
|
|
264
|
-
return { status: Number(status), body: this.config.defaultError.body };
|
|
269
|
+
return { status: Number(status), body: this.config.defaultError.body, headers: {} };
|
|
265
270
|
}
|
|
266
271
|
}
|
|
267
272
|
static getBuilder() {
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { ExpressApp } from './types/ExpressApp.js';
|
|
2
|
+
import { ExpressRequest } from './types/ExpressRequest.js';
|
|
2
3
|
import { DevelopmentUtils } from '../DevelopmentUtils/DevelopmentUtils.js';
|
|
3
4
|
import { OpenApi } from '../../OpenApi.js';
|
|
4
5
|
import { AnyConfig } from '../../types/config/AnyConfig.js';
|
|
@@ -8,6 +9,7 @@ export declare class ExpressWrapper<TRouteTypes extends string, TErrorCodes exte
|
|
|
8
9
|
protected developmentUtils: DevelopmentUtils;
|
|
9
10
|
protected schemaRoute: RoutePath;
|
|
10
11
|
constructor(openApi: OpenApi<TRouteTypes, TErrorCodes, TConfig>);
|
|
12
|
+
requestBodyToString(req: ExpressRequest): Promise<string | undefined>;
|
|
11
13
|
createStoplightRoute(route: RoutePath, expressApp: ExpressApp): void;
|
|
12
14
|
createSwaggerRoute(route: RoutePath, expressApp: ExpressApp): void;
|
|
13
15
|
createSchemaRoute(route: RoutePath, expressApp: ExpressApp): void;
|
|
@@ -8,6 +8,25 @@ export class ExpressWrapper {
|
|
|
8
8
|
this.service = openApi;
|
|
9
9
|
this.developmentUtils = new DevelopmentUtils();
|
|
10
10
|
}
|
|
11
|
+
async requestBodyToString(req) {
|
|
12
|
+
if (!['POST', 'PUT', 'PATCH', 'DELETE'].includes(req.method)) {
|
|
13
|
+
return undefined;
|
|
14
|
+
}
|
|
15
|
+
return new Promise((resolve, reject) => {
|
|
16
|
+
const chunks = [];
|
|
17
|
+
req.on('data', (chunk) => {
|
|
18
|
+
chunks.push(chunk);
|
|
19
|
+
});
|
|
20
|
+
req.on('end', () => {
|
|
21
|
+
const bodyBuffer = Buffer.concat(chunks);
|
|
22
|
+
const bodyString = bodyBuffer.toString();
|
|
23
|
+
resolve(bodyString);
|
|
24
|
+
});
|
|
25
|
+
req.on('error', (error) => {
|
|
26
|
+
reject(error);
|
|
27
|
+
});
|
|
28
|
+
});
|
|
29
|
+
}
|
|
11
30
|
createStoplightRoute(route, expressApp) {
|
|
12
31
|
const handler = async (req, res) => {
|
|
13
32
|
const body = this.developmentUtils.getStoplightHtml(this.schemaRoute);
|
|
@@ -50,13 +69,19 @@ export class ExpressWrapper {
|
|
|
50
69
|
host: req.host,
|
|
51
70
|
pathname: req.originalUrl,
|
|
52
71
|
});
|
|
72
|
+
const body = await this.requestBodyToString(req);
|
|
53
73
|
const openApiRequest = new Request(url, {
|
|
54
|
-
body: req.body,
|
|
55
74
|
headers: headers,
|
|
56
75
|
method: req.method,
|
|
76
|
+
body: body,
|
|
57
77
|
});
|
|
58
78
|
const result = await this.service.processRootRoute(openApiRequest);
|
|
59
|
-
res.status(result.status)
|
|
79
|
+
res.status(result.status);
|
|
80
|
+
res.header('Content-Type', 'application/json');
|
|
81
|
+
for (const header of Object.entries(result.headers)) {
|
|
82
|
+
res.header(header[0], header[1]);
|
|
83
|
+
}
|
|
84
|
+
res.json(result.body);
|
|
60
85
|
};
|
|
61
86
|
const regex = new RegExp(`${route}.*`);
|
|
62
87
|
expressApp.get(regex, handler);
|
|
@@ -5,4 +5,7 @@ export interface ExpressRequest {
|
|
|
5
5
|
protocol: string;
|
|
6
6
|
originalUrl: string;
|
|
7
7
|
host: string;
|
|
8
|
+
on(event: 'data', listener: (chunk: Buffer) => void): this;
|
|
9
|
+
on(event: 'end', listener: () => void): this;
|
|
10
|
+
on(event: 'error', listener: (error: Error) => void): this;
|
|
8
11
|
}
|
|
@@ -6,5 +6,5 @@ import { RouteExtraProps } from '../../types/config/RouteExtraProps.js';
|
|
|
6
6
|
export declare class RoutingFactory<TRouteTypes extends string, TErrorCodes extends string, TConfig extends AnyConfig<TRouteTypes, TErrorCodes>> {
|
|
7
7
|
protected map: TConfig;
|
|
8
8
|
constructor(map: TConfig);
|
|
9
|
-
createRoute<TType extends TRouteTypes, TMethod extends Method, TResponseValidator extends ZodFirstPartySchemaTypes, TQueryValidator extends ZodObject<ZodRawShape> | undefined = undefined, TPathValidator extends ZodObject<ZodRawShape> | undefined = undefined, TBodyValidator extends ZodObject<ZodRawShape> | undefined = undefined>(params: Route<TType, Awaited<ReturnType<TConfig['routes'][TType]['contextFactory']>>, TResponseValidator, TPathValidator, TQueryValidator, TBodyValidator, TMethod> & (RouteExtraProps<TConfig['routes'][TType]['extraProps']>)): Route<TType, Awaited<ReturnType<TConfig['routes'][TType]['contextFactory']>>, TResponseValidator, TPathValidator, TQueryValidator, TBodyValidator>;
|
|
9
|
+
createRoute<TType extends TRouteTypes, TMethod extends Method, TResponseValidator extends ZodFirstPartySchemaTypes, TQueryValidator extends ZodObject<ZodRawShape> | undefined = undefined, TPathValidator extends ZodObject<ZodRawShape> | undefined = undefined, TBodyValidator extends ZodObject<ZodRawShape> | undefined = undefined, TResponseHeadersValidator extends ZodObject<ZodRawShape> | undefined = undefined>(params: Route<TType, Awaited<ReturnType<TConfig['routes'][TType]['contextFactory']>>, TResponseValidator, TPathValidator, TQueryValidator, TBodyValidator, TResponseHeadersValidator, TMethod> & (RouteExtraProps<TConfig['routes'][TType]['extraProps']>)): Route<TType, Awaited<ReturnType<TConfig['routes'][TType]['contextFactory']>>, TResponseValidator, TPathValidator, TQueryValidator, TBodyValidator, TResponseHeadersValidator>;
|
|
10
10
|
}
|
package/dist/types/AnyRoute.d.ts
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
import { ZodObject, ZodRawShape, ZodFirstPartySchemaTypes } from 'zod';
|
|
2
2
|
import { Route } from './Route.js';
|
|
3
|
-
export type AnyRoute<TRouteType extends string> = Route<TRouteType, any, ZodFirstPartySchemaTypes, ZodObject<ZodRawShape> | undefined, ZodObject<ZodRawShape> | undefined, ZodObject<ZodRawShape> | undefined>;
|
|
3
|
+
export type AnyRoute<TRouteType extends string> = Route<TRouteType, any, ZodFirstPartySchemaTypes, ZodObject<ZodRawShape> | undefined, ZodObject<ZodRawShape> | undefined, ZodObject<ZodRawShape> | undefined, ZodObject<ZodRawShape> | undefined>;
|
package/dist/types/Route.d.ts
CHANGED
|
@@ -1,7 +1,13 @@
|
|
|
1
1
|
import { z, ZodFirstPartySchemaTypes, ZodObject, ZodRawShape } from 'zod';
|
|
2
2
|
import { Method } from '../enums/Methods.js';
|
|
3
3
|
import { RoutePath } from './RoutePath.js';
|
|
4
|
-
|
|
4
|
+
type BodyHandlerResponse<T extends ZodFirstPartySchemaTypes> = Promise<z.infer<T>>;
|
|
5
|
+
type FullHandlerRespnse<T extends ZodFirstPartySchemaTypes, THeaders extends ZodObject<ZodRawShape>> = Promise<{
|
|
6
|
+
body: z.infer<T>;
|
|
7
|
+
headers: z.infer<THeaders>;
|
|
8
|
+
}>;
|
|
9
|
+
type HandlerResponse<T extends ZodFirstPartySchemaTypes, TH extends ZodObject<ZodRawShape> | undefined> = TH extends undefined ? BodyHandlerResponse<T> : FullHandlerRespnse<T, Exclude<TH, undefined>>;
|
|
10
|
+
export interface Route<TType extends string, TContext extends object, TResponseValidator extends ZodFirstPartySchemaTypes, TPathValidator extends ZodObject<ZodRawShape> | undefined, TQueryValidator extends ZodObject<ZodRawShape> | undefined, TBodyValidator extends ZodObject<ZodRawShape> | undefined, TResponseHeadersValidator extends ZodObject<ZodRawShape> | undefined, TMethod extends Method = Method> {
|
|
5
11
|
type: TType;
|
|
6
12
|
method: TMethod;
|
|
7
13
|
path: RoutePath;
|
|
@@ -11,6 +17,7 @@ export interface Route<TType extends string, TContext extends object, TResponseV
|
|
|
11
17
|
path?: TPathValidator;
|
|
12
18
|
body?: TMethod extends 'GET' ? never : TBodyValidator;
|
|
13
19
|
response: TResponseValidator;
|
|
20
|
+
responseHeaders?: TResponseHeadersValidator;
|
|
14
21
|
};
|
|
15
22
|
handler: (context: {
|
|
16
23
|
params: {
|
|
@@ -18,5 +25,6 @@ export interface Route<TType extends string, TContext extends object, TResponseV
|
|
|
18
25
|
query: TQueryValidator extends ZodObject<ZodRawShape> ? z.infer<TQueryValidator> : object;
|
|
19
26
|
path: TPathValidator extends ZodObject<ZodRawShape> ? z.infer<TPathValidator> : object;
|
|
20
27
|
};
|
|
21
|
-
} & TContext) =>
|
|
28
|
+
} & TContext) => HandlerResponse<TResponseValidator, TResponseHeadersValidator>;
|
|
22
29
|
}
|
|
30
|
+
export {};
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { Logger } from '../../services/Logger/Logger.js';
|
|
2
2
|
import { LogLevel } from '../../services/Logger/types/LogLevel.js';
|
|
3
|
+
import { AnyRoute } from '../AnyRoute.js';
|
|
3
4
|
import { RoutePath } from '../RoutePath.js';
|
|
4
5
|
import { ErrorConfigMap } from './ErrorConfigMap.js';
|
|
5
6
|
import { ErrorResponse } from './ErrorResponse.js';
|
|
@@ -18,4 +19,9 @@ export type Config<TRouteTypes extends string, TErrorCodes extends string, TErro
|
|
|
18
19
|
servers?: Server[];
|
|
19
20
|
logLevel?: LogLevel;
|
|
20
21
|
handleError?: (e: unknown, req: Request) => ErrorResponse<TErrorCodes, TErrorConfigMap>;
|
|
22
|
+
middleware?: <T extends TRouteTypes>(route: AnyRoute<T>, ctx: Awaited<ReturnType<TRouteContextMap[T]>>) => Promise<{
|
|
23
|
+
body?: unknown;
|
|
24
|
+
status?: number;
|
|
25
|
+
headers?: Record<string, string>;
|
|
26
|
+
}>;
|
|
21
27
|
};
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "snap-on-openapi",
|
|
3
3
|
"author": "Alex Sarychev",
|
|
4
|
-
"version": "1.0.
|
|
4
|
+
"version": "1.0.7",
|
|
5
5
|
"description": "Swiftly build type-checked OpenAPI applications with Zod and TypeScript",
|
|
6
6
|
"type": "module",
|
|
7
7
|
"license": "ISC",
|
|
@@ -16,7 +16,7 @@
|
|
|
16
16
|
},
|
|
17
17
|
"scripts": {
|
|
18
18
|
"build": "rm -rf dist && tsc && tsc-alias && cp -R ./assets ./dist && cp ./README.md ./dist",
|
|
19
|
-
"check": "npm run test:typecheck && npm run lint
|
|
19
|
+
"check": "npm run test:typecheck && npm run lint",
|
|
20
20
|
"test": "vitest --run",
|
|
21
21
|
"test:gha": "npx vitest --run --coverage --reporter=verbose",
|
|
22
22
|
"test:typecheck": "tsc -p tsconfig.test.json",
|