snap-on-openapi 1.0.12 → 1.0.15

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.
Files changed (31) hide show
  1. package/dist/OpenApi.d.ts +4 -7
  2. package/dist/OpenApi.js +79 -53
  3. package/dist/services/ConfigBuilder/types/DefaultConfig.d.ts +8 -1
  4. package/dist/services/ConfigBuilder/types/DefaultConfig.js +20 -6
  5. package/dist/services/RoutingFactory/RoutingFactory.d.ts +3 -1
  6. package/dist/services/RoutingFactory/RoutingFactory.js +26 -1
  7. package/dist/services/RoutingFactory/types/StandardRoute.d.ts +26 -0
  8. package/dist/services/RoutingFactory/types/StandardRoute.js +1 -0
  9. package/dist/services/TestUtils/TestUtils.d.ts +2 -0
  10. package/dist/services/TestUtils/TestUtils.js +4 -0
  11. package/dist/services/TestUtils/utils/TestLogger.d.ts +15 -0
  12. package/dist/services/TestUtils/utils/TestLogger.js +13 -0
  13. package/dist/types/Route.d.ts +2 -8
  14. package/dist/types/RouteResponse.d.ts +5 -0
  15. package/dist/types/RouteResponse.js +1 -0
  16. package/dist/types/config/Config.d.ts +10 -10
  17. package/dist/types/config/ContextParams.d.ts +2 -0
  18. package/dist/types/config/RouteConfig.d.ts +2 -0
  19. package/dist/types/config/RouteHandlerWrapper.d.ts +4 -0
  20. package/dist/types/config/RouteHandlerWrapper.js +1 -0
  21. package/dist/types/events/OnErrorEvent.d.ts +7 -0
  22. package/dist/types/events/OnErrorEvent.js +1 -0
  23. package/dist/types/events/OnHandlerEvent.d.ts +8 -0
  24. package/dist/types/events/OnHandlerEvent.js +1 -0
  25. package/dist/types/events/OnRequestEvent.d.ts +5 -0
  26. package/dist/types/events/OnRequestEvent.js +1 -0
  27. package/dist/types/events/OnResponseEvent.d.ts +5 -0
  28. package/dist/types/events/OnResponseEvent.js +1 -0
  29. package/dist/types/events/OnRouteEvent.d.ts +10 -0
  30. package/dist/types/events/OnRouteEvent.js +1 -0
  31. package/package.json +1 -1
package/dist/OpenApi.d.ts CHANGED
@@ -22,6 +22,7 @@ import { InitialBuilder } from './types/InitialBuilder.js';
22
22
  import { DefaultRouteContextMap } from './services/ConfigBuilder/types/DefaultRouteContextMap.js';
23
23
  import { DefaultRouteParamsMap } from './services/ConfigBuilder/types/DefaultRouteParamsMap.js';
24
24
  import z from 'zod';
25
+ import { RouteResponse } from './types/RouteResponse.js';
25
26
  export declare class OpenApi<TRouteTypes extends string, TErrorCodes extends string, TConfig extends AnyConfig<TRouteTypes, TErrorCodes>> {
26
27
  static readonly builder: InitialBuilder;
27
28
  readonly validators: ValidationUtils;
@@ -46,15 +47,11 @@ export declare class OpenApi<TRouteTypes extends string, TErrorCodes extends str
46
47
  mergePaths(...paths: RoutePath[]): RoutePath;
47
48
  addServer(url: string, description: string): void;
48
49
  protected getRouteForPath(path: string, method: string): AnyRoute<TRouteTypes> | null;
49
- processRootRoute(originalReq: Request): Promise<{
50
- status: number;
51
- body: unknown;
52
- headers: Record<string, string>;
53
- }>;
54
- protected handleError(e: unknown, req: Request): {
50
+ processRootRoute(originalReq: Request): Promise<RouteResponse>;
51
+ protected handleError(e: unknown, req: Request): Promise<{
55
52
  status: number;
56
53
  body: z.TypeOf<import("./index.js").OpenApiErrorConfigMap<TErrorCodes>[TErrorCodes]["responseValidator"]>;
57
54
  headers: {};
58
- };
55
+ }>;
59
56
  protected static getBuilder(): ConfigBuilder<SampleRouteType, ErrorCode, DefaultErrorMap, DefaultRouteParamsMap, DefaultRouteContextMap, DefaultRouteMap, DefaultConfig>;
60
57
  }
package/dist/OpenApi.js CHANGED
@@ -129,6 +129,9 @@ export class OpenApi {
129
129
  }
130
130
  async processRootRoute(originalReq) {
131
131
  try {
132
+ if (this.config.onRequest) {
133
+ await this.config.onRequest({ request: originalReq, logger: this.logger });
134
+ }
132
135
  const url = new URL(originalReq.url);
133
136
  const basePath = this.getBasePath() === '/' ? '' : this.getBasePath();
134
137
  const urlPath = url.pathname.replace(basePath, '');
@@ -176,12 +179,19 @@ export class OpenApi {
176
179
  query: reqQuery,
177
180
  body: body,
178
181
  };
179
- this.logger.info(`Calling route ${route.path}`);
180
- this.logger.info(`${req.method}: ${req.path}`, {
181
- path: req.params,
182
- query: req.query,
183
- body: req.body,
184
- });
182
+ const onRoute = {
183
+ request: originalReq,
184
+ logger: this.logger,
185
+ path: urlPath,
186
+ method: originalReq.method,
187
+ params: pathParams,
188
+ query: reqQuery,
189
+ body: body,
190
+ route: route,
191
+ };
192
+ if (this.config.onRoute) {
193
+ await this.config.onRoute(onRoute);
194
+ }
185
195
  const queryValidator = route.validators.query?.strict() ?? z.object({});
186
196
  const query = queryValidator.safeParse(req.query);
187
197
  if (!query.success) {
@@ -192,80 +202,96 @@ export class OpenApi {
192
202
  if (!path.success) {
193
203
  throw new ValidationError(path.error, ValidationLocation.Path, req.params);
194
204
  }
195
- let response;
196
205
  const containsBody = route.method !== Method.GET;
206
+ let bodyData = {};
197
207
  if (containsBody && route.validators.body) {
198
- const body = route.validators.body.safeParse(req.body);
199
- if (!body.success) {
200
- throw new ValidationError(body.error, ValidationLocation.Body, req.body);
208
+ const bodyResult = route.validators.body.safeParse(req.body);
209
+ if (!bodyResult.success) {
210
+ throw new ValidationError(bodyResult.error, ValidationLocation.Body, req.body);
201
211
  }
202
- const context = await this.config.routes[route.type].contextFactory({
203
- route: route,
204
- request: originalReq,
205
- params: {
206
- query: query.data,
207
- path: path.data,
208
- body: body.data,
209
- },
210
- });
211
- response = await route.handler({
212
- ...context,
213
- params: {
214
- query: query.data,
215
- path: path.data,
216
- body: body.data,
217
- },
218
- });
212
+ bodyData = bodyResult.data;
219
213
  }
220
- else {
221
- const context = await this.config.routes[route.type].contextFactory({
222
- route: route,
223
- request: originalReq,
224
- params: {
225
- query: query.data,
226
- path: path.data,
227
- body: {},
228
- },
229
- });
230
- response = await route.handler({
214
+ const context = await this.config.routes[route.type].contextFactory({
215
+ route: route,
216
+ request: originalReq,
217
+ logger: this.logger,
218
+ params: {
219
+ query: query.data,
220
+ path: path.data,
221
+ body: bodyData,
222
+ },
223
+ });
224
+ const onHandler = {
225
+ ...onRoute,
226
+ validated: {
227
+ query: query.data,
228
+ path: path.data,
229
+ body: bodyData,
230
+ },
231
+ };
232
+ const wrapper = this.config.routes[route.type].handlerWrapper ?? (async (handler) => handler());
233
+ const handler = async () => {
234
+ const response = await route.handler({
231
235
  ...context,
232
236
  params: {
233
237
  query: query.data,
234
238
  path: path.data,
235
- body: {},
236
- }
239
+ body: bodyData,
240
+ },
237
241
  });
238
- }
239
- const finalResponse = route.validators.responseHeaders ? response : { body: response, headers: {} };
242
+ return response;
243
+ };
244
+ const response = await wrapper(handler, {
245
+ route: route,
246
+ request: originalReq,
247
+ logger: this.logger,
248
+ params: {
249
+ query: query.data,
250
+ path: path.data,
251
+ body: bodyData,
252
+ },
253
+ }, context);
240
254
  const finalResponseValidator = z.object({
241
255
  body: route.validators.response ?? z.undefined(),
242
256
  headers: route.validators.responseHeaders?.strict() ?? z.object({}),
257
+ status: z.literal(200),
243
258
  });
259
+ const onResponse = {
260
+ ...onHandler,
261
+ response,
262
+ };
244
263
  if (this.config.disableResponseValidation) {
245
- this.logger.info('Response: 200', finalResponse);
246
- return { status: 200, body: finalResponse.body, headers: finalResponse.headers };
264
+ if (this.config.onResponse) {
265
+ await this.config.onResponse(onResponse);
266
+ }
267
+ return response;
247
268
  }
248
- const validated = finalResponseValidator.safeParse(finalResponse);
269
+ const validated = finalResponseValidator.safeParse(response);
249
270
  if (!validated.success) {
250
- throw new ValidationError(validated.error, ValidationLocation.Response, finalResponse);
271
+ throw new ValidationError(validated.error, ValidationLocation.Response, response);
251
272
  }
252
- this.logger.info('Response: 200', validated.data);
253
- return { status: 200, body: validated.data.body, headers: validated.data.headers };
273
+ if (this.config.onResponse) {
274
+ await this.config.onResponse(onResponse);
275
+ }
276
+ return response;
254
277
  }
255
278
  catch (e) {
256
- return this.handleError(e, originalReq);
279
+ return await this.handleError(e, originalReq);
257
280
  }
258
281
  }
259
- handleError(e, req) {
260
- this.logger.error('Error during request openAPI route handling', e);
282
+ async handleError(e, req) {
261
283
  try {
262
- const response = this.config.handleError ? this.config.handleError(e, { request: req, logger: this.logger }) : this.config.defaultError;
284
+ const event = {
285
+ request: req,
286
+ logger: this.logger,
287
+ error: e,
288
+ };
289
+ const response = this.config.onError ? await this.config.onError(event) : this.config.defaultError;
263
290
  const status = this.config.errors[response.code].status;
264
291
  const valid = this.config.errors[response.code].responseValidator.safeParse(response.body);
265
292
  if (!valid.success) {
266
293
  throw new Error("Error response haven't passed validation");
267
294
  }
268
- this.logger.info(`Response: '${status}'`, response.body);
269
295
  return { status: Number(status), body: response.body, headers: {} };
270
296
  }
271
297
  catch (e) {
@@ -2,6 +2,9 @@ import { ErrorCode } from '../../../enums/ErrorCode.js';
2
2
  import { SampleRouteType } from '../../../enums/SampleRouteType.js';
3
3
  import { Config } from '../../../types/config/Config.js';
4
4
  import { ErrorResponse } from '../../../types/config/ErrorResponse.js';
5
+ import { OnErrorEvent } from '../../../types/events/OnErrorEvent.js';
6
+ import { OnResponseEvent } from '../../../types/events/OnResponseEvent.js';
7
+ import { OnRouteEvent } from '../../../types/events/OnRouteEvent.js';
5
8
  import { RoutePath } from '../../../types/RoutePath.js';
6
9
  import { DefaultErrorMap } from './DefaultErrorMap.js';
7
10
  import { DefaultRouteContextMap } from './DefaultRouteContextMap.js';
@@ -17,6 +20,10 @@ export declare class DefaultConfig implements Config<SampleRouteType, ErrorCode,
17
20
  readonly error: ErrorCode.UnknownError;
18
21
  };
19
22
  };
20
- handleError?: (e: unknown) => ErrorResponse<ErrorCode, DefaultErrorMap>;
23
+ onRequest?: () => Promise<void>;
24
+ onRoute?: (e: OnRouteEvent) => Promise<void>;
25
+ onHandler?: () => Promise<void>;
26
+ onResponse?: (e: OnResponseEvent) => Promise<void>;
27
+ onError?: (e: OnErrorEvent) => Promise<ErrorResponse<ErrorCode, DefaultErrorMap>>;
21
28
  skipDescriptionsCheck?: boolean;
22
29
  }
@@ -14,9 +14,23 @@ export class DefaultConfig {
14
14
  error: ErrorCode.UnknownError,
15
15
  },
16
16
  };
17
- handleError = (e) => {
18
- if (e instanceof ValidationError) {
19
- const zodError = e.getZodError();
17
+ onRequest = () => Promise.resolve();
18
+ onRoute = async (e) => {
19
+ e.logger.info(`Calling route ${e.route.path}`);
20
+ e.logger.info(`${e.method}: ${e.request.url}`, {
21
+ path: e.path,
22
+ query: e.query,
23
+ body: e.body,
24
+ });
25
+ };
26
+ onHandler = () => Promise.resolve();
27
+ onResponse = async (e) => {
28
+ e.logger.info(`Response: ${e.response.status}`, { body: e.response.body, headers: e.response.headers });
29
+ };
30
+ onError = async (e) => {
31
+ e.logger.error('Error during request openAPI route handling', { url: e.request.url, error: e.error });
32
+ if (e.error instanceof ValidationError) {
33
+ const zodError = e.error.getZodError();
20
34
  const map = [];
21
35
  for (const issue of zodError.issues) {
22
36
  map.push({
@@ -24,18 +38,18 @@ export class DefaultConfig {
24
38
  message: issue.message,
25
39
  });
26
40
  }
27
- if (e.getLocation() !== ValidationLocation.Response) {
41
+ if (e.error.getLocation() !== ValidationLocation.Response) {
28
42
  const response = {
29
43
  error: {
30
44
  code: ErrorCode.ValidationFailed,
31
- location: e.getLocation(),
45
+ location: e.error.getLocation(),
32
46
  fieldErrors: map,
33
47
  },
34
48
  };
35
49
  return { code: ErrorCode.ValidationFailed, body: response };
36
50
  }
37
51
  }
38
- if (e instanceof BuiltInError && e.getCode() === ErrorCode.NotFound) {
52
+ if (e.error instanceof BuiltInError && e.error.getCode() === ErrorCode.NotFound) {
39
53
  return { code: ErrorCode.NotFound, body: { error: ErrorCode.NotFound } };
40
54
  }
41
55
  const unknownError = {
@@ -3,8 +3,10 @@ import { Method } from '../../enums/Methods.js';
3
3
  import { Route } from '../../types/Route.js';
4
4
  import { AnyConfig } from '../../types/config/AnyConfig.js';
5
5
  import { RouteExtraProps } from '../../types/config/RouteExtraProps.js';
6
+ import { StandardRoute } from './types/StandardRoute.js';
6
7
  export declare class RoutingFactory<TRouteTypes extends string, TErrorCodes extends string, TConfig extends AnyConfig<TRouteTypes, TErrorCodes>> {
7
8
  protected map: TConfig;
8
9
  constructor(map: TConfig);
9
- createRoute<TType extends TRouteTypes, TMethod extends Method, TResponseValidator extends ZodFirstPartySchemaTypes | undefined = undefined, TQueryValidator extends ZodObject<ZodRawShape> | undefined = undefined, TPathValidator extends ZodObject<ZodRawShape> | undefined = undefined, TBodyValidator extends ZodFirstPartySchemaTypes | 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
+ createCustomRoute<TType extends TRouteTypes, TMethod extends Method, TResponseValidator extends ZodFirstPartySchemaTypes | undefined = undefined, TQueryValidator extends ZodObject<ZodRawShape> | undefined = undefined, TPathValidator extends ZodObject<ZodRawShape> | undefined = undefined, TBodyValidator extends ZodFirstPartySchemaTypes | 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>;
11
+ createRoute<TType extends TRouteTypes, TMethod extends Method, TResponseValidator extends ZodFirstPartySchemaTypes | undefined = undefined, TQueryValidator extends ZodObject<ZodRawShape> | undefined = undefined, TPathValidator extends ZodObject<ZodRawShape> | undefined = undefined, TBodyValidator extends ZodFirstPartySchemaTypes | undefined = undefined, TResponseHeadersValidator extends ZodObject<ZodRawShape> | undefined = undefined>(params: StandardRoute<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, TResponseHeadersValidator>;
10
12
  }
@@ -3,7 +3,7 @@ export class RoutingFactory {
3
3
  constructor(map) {
4
4
  this.map = map;
5
5
  }
6
- createRoute(params) {
6
+ createCustomRoute(params) {
7
7
  const result = {
8
8
  ...params,
9
9
  method: params.method,
@@ -21,4 +21,29 @@ export class RoutingFactory {
21
21
  };
22
22
  return result;
23
23
  }
24
+ createRoute(params) {
25
+ const newHandler = async (ctx) => {
26
+ const body = await params.handler(ctx);
27
+ return {
28
+ body: body,
29
+ status: 200,
30
+ headers: {},
31
+ };
32
+ };
33
+ const result = {
34
+ ...params,
35
+ method: params.method,
36
+ type: params.type,
37
+ path: params.path,
38
+ description: params.description,
39
+ validators: {
40
+ query: params.validators.query,
41
+ path: params.validators.path,
42
+ response: params.validators.response,
43
+ body: params.validators.body,
44
+ },
45
+ handler: newHandler,
46
+ };
47
+ return result;
48
+ }
24
49
  }
@@ -0,0 +1,26 @@
1
+ import { z, ZodFirstPartySchemaTypes, ZodObject, ZodRawShape } from 'zod';
2
+ import { Method } from '../../../enums/Methods.js';
3
+ import { RoutePath } from '../../../types/RoutePath.js';
4
+ type BodyHandlerResponse<T extends ZodFirstPartySchemaTypes | undefined = undefined> = Promise<T extends undefined ? void : z.infer<Exclude<T, undefined>>>;
5
+ export interface StandardRoute<TType extends string, TContext extends object, TResponseValidator extends ZodFirstPartySchemaTypes | undefined, TPathValidator extends ZodObject<ZodRawShape> | undefined, TQueryValidator extends ZodObject<ZodRawShape> | undefined, TBodyValidator extends ZodFirstPartySchemaTypes | undefined, TMethod extends Method = Method> {
6
+ tags?: string[];
7
+ operationId?: string;
8
+ type: TType;
9
+ method: TMethod;
10
+ path: RoutePath;
11
+ description: string;
12
+ validators: {
13
+ query?: TQueryValidator;
14
+ path?: TPathValidator;
15
+ body?: TMethod extends 'GET' ? never : TBodyValidator;
16
+ response?: TResponseValidator;
17
+ };
18
+ handler: (context: {
19
+ params: {
20
+ body: TBodyValidator extends ZodFirstPartySchemaTypes ? z.infer<TBodyValidator> : object;
21
+ query: TQueryValidator extends ZodObject<ZodRawShape> ? z.infer<TQueryValidator> : object;
22
+ path: TPathValidator extends ZodObject<ZodRawShape> ? z.infer<TPathValidator> : object;
23
+ };
24
+ } & TContext) => BodyHandlerResponse<TResponseValidator>;
25
+ }
26
+ export {};
@@ -2,6 +2,7 @@ import { OpenApi } from '../../OpenApi.js';
2
2
  import { Method } from '../../enums/Methods.js';
3
3
  import { SampleRouteType } from '../../enums/SampleRouteType.js';
4
4
  import { RoutePath } from '../../types/RoutePath.js';
5
+ import { TestLogger } from './utils/TestLogger.js';
5
6
  export declare class TestUtils {
6
7
  static createRequest(route: RoutePath, method?: Method, body?: object): Request;
7
8
  static sendRequest(api: OpenApi<any, any, any>, route: RoutePath, method?: Method, body?: object): Promise<{
@@ -10,4 +11,5 @@ export declare class TestUtils {
10
11
  }>;
11
12
  static createOpenApi(): OpenApi<SampleRouteType, import("../../index.js").OpenApiErrorCode, import("../../index.js").OpenApiDefaultConfig>;
12
13
  static awaitGeneric<T>(timeoutMs: number, intervalMs: number, callback: () => Promise<T | null>): Promise<T | null>;
14
+ static getTestLogger(): TestLogger;
13
15
  }
@@ -2,6 +2,7 @@ import z from 'zod';
2
2
  import { OpenApi } from '../../OpenApi.js';
3
3
  import { Method } from '../../enums/Methods.js';
4
4
  import { SampleRouteType } from '../../enums/SampleRouteType.js';
5
+ import { TestLogger } from './utils/TestLogger.js';
5
6
  export class TestUtils {
6
7
  static createRequest(route, method = Method.GET, body) {
7
8
  const request = new Request(`http://localhost${route}`, {
@@ -45,4 +46,7 @@ export class TestUtils {
45
46
  } while (now < deadline);
46
47
  return null;
47
48
  }
49
+ static getTestLogger() {
50
+ return new TestLogger('TestUtils');
51
+ }
48
52
  }
@@ -0,0 +1,15 @@
1
+ import { Logger } from '../../Logger/Logger.js';
2
+ export declare class TestLogger extends Logger {
3
+ private messages;
4
+ popMessage(): {
5
+ message: string;
6
+ level: string;
7
+ data?: object;
8
+ } | undefined;
9
+ shiftMessage(): {
10
+ message: string;
11
+ level: string;
12
+ data?: object;
13
+ } | undefined;
14
+ protected log(message: string, level: string, data?: object): void;
15
+ }
@@ -0,0 +1,13 @@
1
+ import { Logger } from '../../Logger/Logger.js';
2
+ export class TestLogger extends Logger {
3
+ messages = [];
4
+ popMessage() {
5
+ return this.messages.pop();
6
+ }
7
+ shiftMessage() {
8
+ return this.messages.shift();
9
+ }
10
+ log(message, level, data) {
11
+ this.messages.push({ message, level, data });
12
+ }
13
+ }
@@ -1,12 +1,7 @@
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
- type BodyHandlerResponse<T extends ZodFirstPartySchemaTypes | undefined = undefined> = Promise<T extends undefined ? undefined : z.infer<Exclude<T, undefined>>>;
5
- type FullHandlerRespnse<T extends ZodFirstPartySchemaTypes | undefined, THeaders extends ZodObject<ZodRawShape> | undefined> = Promise<{
6
- body: Awaited<BodyHandlerResponse<T>>;
7
- headers: z.infer<Exclude<THeaders, undefined>>;
8
- }>;
9
- type HandlerResponse<T extends ZodFirstPartySchemaTypes | undefined, TH extends ZodObject<ZodRawShape> | undefined> = TH extends undefined ? BodyHandlerResponse<T> : FullHandlerRespnse<T, Exclude<TH, undefined>>;
4
+ import { RouteResponse } from './RouteResponse.js';
10
5
  export interface Route<TType extends string, TContext extends object, TResponseValidator extends ZodFirstPartySchemaTypes | undefined, TPathValidator extends ZodObject<ZodRawShape> | undefined, TQueryValidator extends ZodObject<ZodRawShape> | undefined, TBodyValidator extends ZodFirstPartySchemaTypes | undefined, TResponseHeadersValidator extends ZodObject<ZodRawShape> | undefined, TMethod extends Method = Method> {
11
6
  tags?: string[];
12
7
  operationId?: string;
@@ -27,6 +22,5 @@ export interface Route<TType extends string, TContext extends object, TResponseV
27
22
  query: TQueryValidator extends ZodObject<ZodRawShape> ? z.infer<TQueryValidator> : object;
28
23
  path: TPathValidator extends ZodObject<ZodRawShape> ? z.infer<TPathValidator> : object;
29
24
  };
30
- } & TContext) => HandlerResponse<TResponseValidator, TResponseHeadersValidator>;
25
+ } & TContext) => Promise<RouteResponse>;
31
26
  }
32
- export {};
@@ -0,0 +1,5 @@
1
+ export interface RouteResponse {
2
+ body: unknown;
3
+ status: number;
4
+ headers: Record<string, string>;
5
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -1,7 +1,6 @@
1
1
  import { ZodOpenApiVersion } from 'zod-openapi';
2
2
  import { Logger } from '../../services/Logger/Logger.js';
3
3
  import { LogLevel } from '../../services/Logger/types/LogLevel.js';
4
- import { AnyRoute } from '../AnyRoute.js';
5
4
  import { RoutePath } from '../RoutePath.js';
6
5
  import { ErrorConfigMap } from './ErrorConfigMap.js';
7
6
  import { ErrorResponse } from './ErrorResponse.js';
@@ -9,6 +8,11 @@ import { RouteConfigMap } from './RouteConfigMap.js';
9
8
  import { RouteContextMap } from './RouteContextMap.js';
10
9
  import { RouteExtraPropsMap } from './RouteExtraPropsMap.js';
11
10
  import { Server } from './Server.js';
11
+ import { OnErrorEvent } from '../events/OnErrorEvent.js';
12
+ import { OnHandlerEvent } from '../events/OnHandlerEvent.js';
13
+ import { OnRequestEvent } from '../events/OnRequestEvent.js';
14
+ import { OnResponseEvent } from '../events/OnResponseEvent.js';
15
+ import { OnRouteEvent } from '../events/OnRouteEvent.js';
12
16
  export type Config<TRouteTypes extends string, TErrorCodes extends string, TErrorConfigMap extends ErrorConfigMap<TErrorCodes>, TRouteParamMap extends RouteExtraPropsMap<TRouteTypes>, TRouteContextMap extends RouteContextMap<TRouteTypes, TRouteParamMap>, TRouteConfigMap extends RouteConfigMap<TRouteTypes, TErrorCodes, TRouteParamMap, TRouteContextMap>> = {
13
17
  disableResponseValidation?: boolean;
14
18
  logger?: Logger;
@@ -28,13 +32,9 @@ export type Config<TRouteTypes extends string, TErrorCodes extends string, TErro
28
32
  apiVersion?: string;
29
33
  servers?: Server[];
30
34
  logLevel?: LogLevel;
31
- handleError?: (e: unknown, context: {
32
- request: Request;
33
- logger: Logger;
34
- }) => ErrorResponse<TErrorCodes, TErrorConfigMap>;
35
- middleware?: <T extends TRouteTypes>(route: AnyRoute<T>, ctx: Awaited<ReturnType<TRouteContextMap[T]>>) => Promise<{
36
- body?: unknown;
37
- status?: number;
38
- headers?: Record<string, string>;
39
- }>;
35
+ onRequest?: (e: OnRequestEvent) => Promise<void>;
36
+ onRoute?: (e: OnRouteEvent) => Promise<void>;
37
+ onHandler?: (e: OnHandlerEvent) => Promise<void>;
38
+ onResponse?: (e: OnResponseEvent) => Promise<void>;
39
+ onError?: (e: OnErrorEvent) => Promise<ErrorResponse<TErrorCodes, TErrorConfigMap>>;
40
40
  };
@@ -1,9 +1,11 @@
1
1
  import { ZodObject, ZodRawShape } from 'zod';
2
2
  import { AnyRoute } from '../AnyRoute.js';
3
3
  import { RouteExtraProps } from './RouteExtraProps.js';
4
+ import { Logger } from '../../services/Logger/Logger.js';
4
5
  export type ContextParams<TRouteType extends string, TExtraProps extends ZodObject<ZodRawShape> | undefined> = {
5
6
  route: AnyRoute<TRouteType> & RouteExtraProps<TExtraProps>;
6
7
  request: Request;
8
+ logger: Logger;
7
9
  params: {
8
10
  body: unknown;
9
11
  query: unknown;
@@ -1,9 +1,11 @@
1
1
  import { ZodObject, ZodRawShape } from 'zod';
2
2
  import { ContextParams } from './ContextParams.js';
3
+ import { RouteHandlerWrapper } from './RouteHandlerWrapper.js';
3
4
  export type RouteConfig<TRouteType extends string, TErrorCodes extends string, TExtraProps extends ZodObject<ZodRawShape> | undefined = ZodObject<ZodRawShape> | undefined, TContext extends object | undefined = object | undefined> = {
4
5
  authorization: boolean;
5
6
  extraProps: TExtraProps;
6
7
  contextFactory: (params: ContextParams<TRouteType, TExtraProps>) => Promise<TContext>;
8
+ handlerWrapper?: RouteHandlerWrapper<TRouteType, TExtraProps, TContext>;
7
9
  errors?: {
8
10
  [key in TErrorCodes]?: true;
9
11
  };
@@ -0,0 +1,4 @@
1
+ import { ZodObject, ZodRawShape } from 'zod';
2
+ import { ContextParams } from './ContextParams.js';
3
+ import { RouteResponse } from '../RouteResponse.js';
4
+ export type RouteHandlerWrapper<TRouteType extends string, TExtraProps extends ZodObject<ZodRawShape> | undefined, TContext extends object | undefined> = (handler: () => Promise<RouteResponse>, params: ContextParams<TRouteType, TExtraProps>, context: TContext) => Promise<RouteResponse>;
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,7 @@
1
+ import { Logger } from '../../services/Logger/Logger.js';
2
+ import { OnResponseEvent } from './OnResponseEvent.js';
3
+ export interface OnErrorEvent extends Partial<OnResponseEvent> {
4
+ request: Request;
5
+ logger: Logger;
6
+ error: unknown;
7
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,8 @@
1
+ import { OnRouteEvent } from './OnRouteEvent.js';
2
+ export interface OnHandlerEvent extends OnRouteEvent {
3
+ validated: {
4
+ query: Record<string, unknown>;
5
+ path: Record<string, unknown>;
6
+ body: unknown;
7
+ };
8
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,5 @@
1
+ import { Logger } from '../../services/Logger/Logger.js';
2
+ export interface OnRequestEvent {
3
+ request: Request;
4
+ logger: Logger;
5
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,5 @@
1
+ import { RouteResponse } from '../RouteResponse.js';
2
+ import { OnHandlerEvent } from './OnHandlerEvent.js';
3
+ export interface OnResponseEvent extends OnHandlerEvent {
4
+ response: RouteResponse;
5
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,10 @@
1
+ import { AnyRoute } from '../AnyRoute.js';
2
+ import { OnRequestEvent } from './OnRequestEvent.js';
3
+ export interface OnRouteEvent extends OnRequestEvent {
4
+ path: string;
5
+ method: string;
6
+ params: Record<string, string>;
7
+ query: Record<string, string | string[]>;
8
+ body: unknown;
9
+ route: AnyRoute<string>;
10
+ }
@@ -0,0 +1 @@
1
+ export {};
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.12",
4
+ "version": "1.0.15",
5
5
  "description": "Swiftly build type-checked OpenAPI applications with Zod and TypeScript",
6
6
  "type": "module",
7
7
  "license": "ISC",