@lucaapp/service-utils 1.40.3 → 1.41.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.
@@ -9,6 +9,7 @@ require("express-async-errors");
9
9
  const swagger_ui_express_1 = __importDefault(require("swagger-ui-express"));
10
10
  const zod_to_openapi_1 = require("@asteasolutions/zod-to-openapi");
11
11
  const endpoint_1 = require("./endpoint");
12
+ const openapi_1 = require("./openapi");
12
13
  class Api {
13
14
  constructor(options = {}) {
14
15
  this.router = options.router || (0, express_1.Router)();
@@ -32,23 +33,23 @@ class Api {
32
33
  }
33
34
  get(path, summary, options, handler) {
34
35
  (0, endpoint_1.mountEndpoint)(this.router, 'get', path, options, handler, this.debug);
35
- (0, endpoint_1.registerEndpoint)(this.registry, 'get', this.prefixPath, path, summary, options, this.tags, this.debug);
36
+ (0, openapi_1.registerEndpoint)(this.registry, 'get', this.prefixPath, path, summary, options, this.tags, this.debug);
36
37
  }
37
38
  post(path, summary, options, handler) {
38
39
  (0, endpoint_1.mountEndpoint)(this.router, 'post', path, options, handler, this.debug);
39
- (0, endpoint_1.registerEndpoint)(this.registry, 'post', this.prefixPath, path, summary, options, this.tags, this.debug);
40
+ (0, openapi_1.registerEndpoint)(this.registry, 'post', this.prefixPath, path, summary, options, this.tags, this.debug);
40
41
  }
41
42
  patch(path, summary, options, handler) {
42
43
  (0, endpoint_1.mountEndpoint)(this.router, 'patch', path, options, handler, this.debug);
43
- (0, endpoint_1.registerEndpoint)(this.registry, 'patch', this.prefixPath, path, summary, options, this.tags, this.debug);
44
+ (0, openapi_1.registerEndpoint)(this.registry, 'patch', this.prefixPath, path, summary, options, this.tags, this.debug);
44
45
  }
45
46
  put(path, summary, options, handler) {
46
47
  (0, endpoint_1.mountEndpoint)(this.router, 'put', path, options, handler, this.debug);
47
- (0, endpoint_1.registerEndpoint)(this.registry, 'put', this.prefixPath, path, summary, options, this.tags, this.debug);
48
+ (0, openapi_1.registerEndpoint)(this.registry, 'put', this.prefixPath, path, summary, options, this.tags, this.debug);
48
49
  }
49
50
  delete(path, summary, options, handler) {
50
51
  (0, endpoint_1.mountEndpoint)(this.router, 'delete', path, options, handler, this.debug);
51
- (0, endpoint_1.registerEndpoint)(this.registry, 'delete', this.prefixPath, path, summary, options, this.tags, this.debug);
52
+ (0, openapi_1.registerEndpoint)(this.registry, 'delete', this.prefixPath, path, summary, options, this.tags, this.debug);
52
53
  }
53
54
  generateOpenAPISpec() {
54
55
  const generator = new zod_to_openapi_1.OpenAPIGenerator(this.registry.definitions, this.openApiVersion);
@@ -1,9 +1,7 @@
1
1
  import { Router } from 'express';
2
- import { OpenAPIRegistry } from '@asteasolutions/zod-to-openapi';
3
2
  import { Middleware, EndpointResponseSchema } from './types/middleware';
4
3
  import { EndpointOptions, EndpointHandler } from './types/endpoint';
5
4
  import { ZodObjectSchemaOrUndefined } from './types/utils';
6
5
  type HttpMethod = 'get' | 'post' | 'put' | 'delete' | 'patch';
7
6
  export declare const mountEndpoint: <TResponseSchemas extends readonly EndpointResponseSchema[], TRequestBodySchema extends ZodObjectSchemaOrUndefined, TRequestParamsSchema extends ZodObjectSchemaOrUndefined, TRequestQuerySchema extends ZodObjectSchemaOrUndefined, TRequestHeadersSchema extends ZodObjectSchemaOrUndefined, TMiddlewares extends readonly Middleware<any, any, any, any, any, any>[] | undefined = undefined>(router: Router, method: HttpMethod, path: string, options: EndpointOptions<TResponseSchemas, TRequestBodySchema, TRequestParamsSchema, TRequestQuerySchema, TRequestHeadersSchema, TMiddlewares>, handler: EndpointHandler<TResponseSchemas, TRequestBodySchema, TRequestParamsSchema, TRequestQuerySchema, TRequestHeadersSchema, TMiddlewares>, debug: boolean) => void;
8
- export declare const registerEndpoint: (registry: OpenAPIRegistry, method: HttpMethod, prefixPath: string, path: string, summary: string, options: EndpointOptions<any, any, any, any, any, any>, tags: string[], debug: boolean) => void;
9
7
  export {};
@@ -3,99 +3,165 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
3
3
  return (mod && mod.__esModule) ? mod : { "default": mod };
4
4
  };
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
- exports.registerEndpoint = exports.mountEndpoint = void 0;
6
+ exports.mountEndpoint = void 0;
7
7
  const zod_1 = require("zod");
8
8
  const assert_1 = __importDefault(require("assert"));
9
9
  const http_1 = require("./types/http");
10
- const mapExpressParamsToOpenAPIParams = (path) => path.replaceAll(/\:([^\:\/]+)/g, '{$1}');
11
- const buildResponseConfig = (responseOptions) => {
12
- const responses = {};
13
- responseOptions.forEach(response => {
14
- const isAlreadyPresent = !!responses[String(response.status)];
15
- if (!isAlreadyPresent) {
16
- responses[String(response.status)] = {
17
- description: response.description,
18
- ...(!(response.schema instanceof zod_1.z.ZodVoid) && {
19
- content: {
20
- 'application/json': {
21
- schema: response.schema,
22
- },
23
- },
24
- }),
25
- };
26
- return;
27
- }
28
- const existingResponse = responses[response.status];
29
- existingResponse.description =
30
- existingResponse.description + ' | ' + response.description;
31
- // No Content merge
32
- if (!response.schema) {
33
- return;
34
- }
35
- // No Content
36
- if (!existingResponse.content ||
37
- !existingResponse.content['application/json']) {
38
- return;
39
- }
40
- existingResponse.content['application/json'].schema =
41
- existingResponse.content['application/json'].schema.or(response.schema);
10
+ const sendBadContextError = (response, error, debug) => {
11
+ response.err = error;
12
+ return response.status(500).send({
13
+ error: http_1.HTTPStatus.INTERNAL_SERVER_ERROR,
14
+ message: 'Bad Context',
15
+ ...(debug && {
16
+ debug: {
17
+ name: error.name,
18
+ message: error.message,
19
+ stack: error.stack,
20
+ issues: error.issues,
21
+ },
22
+ }),
42
23
  });
43
- return responses;
44
24
  };
45
- const isTypedError = (error) => {
46
- if (!(error instanceof Error))
47
- return false;
48
- // can't access possibly existing type property without casting to any
49
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
50
- if (typeof error.type !== 'string')
51
- return false;
52
- return true;
53
- };
54
- const validateAndSendResponse = async (expressResponse, response, validResponses) => {
55
- (0, assert_1.default)(typeof response.status === 'number');
56
- // get schema respective to status code
57
- const responseSchema = validResponses.find(responseSchema => responseSchema.status === response.status);
58
- const schema = responseSchema?.schema;
59
- const isZodSchema = schema instanceof zod_1.z.ZodSchema;
60
- const isZodVoid = schema instanceof zod_1.z.ZodVoid;
61
- (0, assert_1.default)(isZodSchema || isZodVoid);
62
- // validate response schema
63
- const validatedResponseBody = await schema.parseAsync(response.body);
64
- if (validatedResponseBody === undefined) {
65
- // No Content Response
66
- return expressResponse.status(response.status).end();
67
- }
68
- expressResponse.status(response.status).send(validatedResponseBody);
69
- };
70
- const sendBadResponseError = (response, error) => {
25
+ const sendBadResponseError = (response, error, debug) => {
26
+ response.err = error;
71
27
  return response.status(500).send({
72
28
  error: http_1.HTTPStatus.INTERNAL_SERVER_ERROR,
73
- message: 'Invalid Response',
29
+ message: 'Bad Response',
30
+ ...(debug && {
31
+ debug: {
32
+ name: error.name,
33
+ message: error.message,
34
+ stack: error.stack,
35
+ issues: error.issues,
36
+ },
37
+ }),
38
+ });
39
+ };
40
+ const sendBadRequestError = (response, error) => {
41
+ response.err = error;
42
+ return response.status(400).send({
43
+ error: http_1.HTTPStatus.BAD_REQUEST,
44
+ message: error.message,
74
45
  issues: error.issues,
75
46
  });
76
47
  };
77
48
  const sendKnownErrorResponse = (response, statusCode, error, debug) => {
49
+ response.err = error;
78
50
  response.status(statusCode).send({
79
51
  error: error.type,
80
52
  message: error.message,
81
- ...(debug && { stack: error.stack }),
53
+ ...(debug && {
54
+ debug: {
55
+ name: error.name,
56
+ message: error.message,
57
+ stack: error.stack,
58
+ meta: error.meta,
59
+ },
60
+ }),
82
61
  });
83
62
  };
84
- const sendBadRequestError = (response, error) => {
85
- return response.status(400).send({
86
- error: http_1.HTTPStatus.BAD_REQUEST,
87
- message: error.message,
88
- issues: error.issues,
63
+ const sendUnknownErrorResponse = (response, error, debug) => {
64
+ if (error instanceof Error) {
65
+ response.err = error;
66
+ // try to pass forward meta if it exists even for unknown/unmapped errors
67
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
68
+ const meta = error.meta;
69
+ response.status(500).send({
70
+ error: http_1.HTTPStatus.INTERNAL_SERVER_ERROR,
71
+ message: 'Internal Server Error',
72
+ ...(debug && {
73
+ debug: {
74
+ name: error.name,
75
+ message: error.message,
76
+ stack: error.stack,
77
+ meta,
78
+ },
79
+ }),
80
+ });
81
+ return;
82
+ }
83
+ response.status(500).send({
84
+ error: http_1.HTTPStatus.INTERNAL_SERVER_ERROR,
85
+ message: 'Internal Server Error',
86
+ ...(debug && {
87
+ debug: {
88
+ message: 'Non Error Object',
89
+ error: error,
90
+ },
91
+ }),
89
92
  });
90
93
  };
91
- const handleMiddleware = async (middleware, context, request, response, debug) => new Promise(async (resolve, reject) => {
94
+ const sendErrorResponse = (response, error, knownErrors, debug) => {
95
+ if (isTypedError(error) && knownErrors && knownErrors[error.type]) {
96
+ const statusCode = knownErrors[error.type];
97
+ sendKnownErrorResponse(response, statusCode, error, debug);
98
+ return;
99
+ }
100
+ sendUnknownErrorResponse(response, error, debug);
101
+ return;
102
+ };
103
+ const isTypedError = (error) => {
104
+ if (!(error instanceof Error))
105
+ return false;
106
+ // can't access possibly existing type property without casting to any
107
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
108
+ if (typeof error.type !== 'string')
109
+ return false;
110
+ return true;
111
+ };
112
+ const validateAndSendResponse = async (expressResponse, response, validResponses, debug) => {
92
113
  try {
93
- const handlerRequestBody = ((await middleware.options.schemas?.body?.parseAsync(request.body)) ||
94
- undefined);
95
- const handlerRequestParams = ((await middleware.options.schemas?.params?.parseAsync(request.params)) || undefined);
96
- const handlerRequestQuery = ((await middleware.options.schemas?.query?.parseAsync(request.query)) ||
97
- undefined);
98
- const handlerRequestHeaders = ((await middleware.options.schemas?.headers?.parseAsync(request.headers)) || undefined);
114
+ (0, assert_1.default)(typeof response.status === 'number');
115
+ // get schema respective to status code
116
+ const responseSchema = validResponses.find(responseSchema => responseSchema.status === response.status);
117
+ const schema = responseSchema?.schema;
118
+ const isZodSchema = schema instanceof zod_1.z.ZodSchema;
119
+ const isZodVoid = schema instanceof zod_1.z.ZodVoid;
120
+ (0, assert_1.default)(isZodSchema || isZodVoid);
121
+ // validate response schema
122
+ const validatedResponseBody = await schema.parseAsync(response.body);
123
+ if (validatedResponseBody === undefined) {
124
+ // No Content Response
125
+ expressResponse.status(response.status).end();
126
+ return;
127
+ }
128
+ expressResponse.status(response.status).send(validatedResponseBody);
129
+ }
130
+ catch (error) {
131
+ if (error instanceof zod_1.ZodError) {
132
+ sendBadResponseError(expressResponse, error, debug);
133
+ return;
134
+ }
135
+ sendErrorResponse(expressResponse, error, undefined, debug);
136
+ }
137
+ };
138
+ const handleMiddleware = async (middleware, context, request, response, debug) =>
139
+ // resolves true when next middleware should continue
140
+ // resolves false when a response was sent and next middleware should not be called
141
+ new Promise(async (resolve) => {
142
+ try {
143
+ let handlerRequestBody;
144
+ let handlerRequestParams;
145
+ let handlerRequestQuery;
146
+ let handlerRequestHeaders;
147
+ try {
148
+ handlerRequestBody =
149
+ ((await middleware.options.schemas?.body?.parseAsync(request.body)) ||
150
+ undefined);
151
+ handlerRequestParams =
152
+ ((await middleware.options.schemas?.params?.parseAsync(request.params)) || undefined);
153
+ handlerRequestQuery =
154
+ ((await middleware.options.schemas?.query?.parseAsync(request.query)) || undefined);
155
+ handlerRequestHeaders =
156
+ ((await middleware.options.schemas?.headers?.parseAsync(request.headers)) || undefined);
157
+ }
158
+ catch (error) {
159
+ if (error instanceof zod_1.ZodError) {
160
+ sendBadRequestError(response, error);
161
+ return resolve(false);
162
+ }
163
+ throw error;
164
+ }
99
165
  const { ip, baseUrl, originalUrl, route, method } = request;
100
166
  const handlerRequest = {
101
167
  body: handlerRequestBody,
@@ -109,17 +175,8 @@ const handleMiddleware = async (middleware, context, request, response, debug) =
109
175
  path: route.path,
110
176
  };
111
177
  await middleware.handler(handlerRequest, async (middlewareResponse) => {
112
- try {
113
- await validateAndSendResponse(response, middlewareResponse, middleware.options.responses);
114
- resolve(false);
115
- }
116
- catch (error) {
117
- if (error instanceof zod_1.ZodError) {
118
- sendBadResponseError(response, error);
119
- return resolve(false);
120
- }
121
- reject(error);
122
- }
178
+ await validateAndSendResponse(response, middlewareResponse, middleware.options.responses, debug);
179
+ resolve(false);
123
180
  }, async (returnedContext) => {
124
181
  try {
125
182
  const validatedContext = await middleware.options.schemas?.context?.parseAsync(returnedContext);
@@ -128,40 +185,40 @@ const handleMiddleware = async (middleware, context, request, response, debug) =
128
185
  }
129
186
  catch (error) {
130
187
  if (error instanceof zod_1.ZodError) {
131
- sendBadResponseError(response, error);
188
+ sendBadContextError(response, error, debug);
132
189
  return resolve(false);
133
190
  }
134
- reject(error);
191
+ sendErrorResponse(response, error, undefined, debug);
192
+ resolve(false);
135
193
  }
136
194
  });
137
195
  }
138
196
  catch (error) {
139
- if (error instanceof zod_1.ZodError) {
140
- sendBadRequestError(response, error);
141
- return resolve(false);
142
- }
143
- if (isTypedError(error) &&
144
- middleware.options.errors &&
145
- middleware.options.errors[error.type]) {
146
- const statusCode = middleware.options.errors[error.type];
147
- sendKnownErrorResponse(response, statusCode, error, debug);
148
- return resolve(false);
149
- }
150
- // bubble up unknown errors
151
- reject(error);
197
+ sendErrorResponse(response, error, middleware.options.errors, debug);
198
+ resolve(false);
152
199
  }
153
200
  });
154
201
  const mountEndpoint = (router, method, path, options, handler, debug) => {
155
202
  (0, assert_1.default)(options.responses.length > 0, 'You need to specify at least one response');
156
- router[method](path, async (request, response, next) => {
203
+ router[method](path, async (request, response) => {
157
204
  try {
158
- // parse request schemas
159
- const handlerRequestBody = ((await options.schemas?.body?.parseAsync(request.body)) || undefined);
160
- const handlerRequestParams = ((await options.schemas?.params?.parseAsync(request.params)) ||
161
- undefined);
162
- const handlerRequestQuery = ((await options.schemas?.query?.parseAsync(request.query)) || undefined);
163
- const handlerRequestHeaders = ((await options.schemas?.headers?.parseAsync(request.headers)) ||
164
- undefined);
205
+ let handlerRequestBody;
206
+ let handlerRequestParams;
207
+ let handlerRequestQuery;
208
+ let handlerRequestHeaders;
209
+ try {
210
+ // parse request schemas
211
+ handlerRequestBody = ((await options.schemas?.body?.parseAsync(request.body)) || undefined);
212
+ handlerRequestParams = ((await options.schemas?.params?.parseAsync(request.params)) || undefined);
213
+ handlerRequestQuery = ((await options.schemas?.query?.parseAsync(request.query)) || undefined);
214
+ handlerRequestHeaders = ((await options.schemas?.headers?.parseAsync(request.headers)) || undefined);
215
+ }
216
+ catch (error) {
217
+ if (error instanceof zod_1.ZodError) {
218
+ return sendBadRequestError(response, error);
219
+ }
220
+ throw error;
221
+ }
165
222
  const handlerRequest = {
166
223
  body: handlerRequestBody,
167
224
  params: handlerRequestParams,
@@ -177,148 +234,12 @@ const mountEndpoint = (router, method, path, options, handler, debug) => {
177
234
  }
178
235
  }
179
236
  await handler(handlerRequest, ctx, async (controllerResponse) => {
180
- try {
181
- await validateAndSendResponse(response, controllerResponse, options.responses);
182
- }
183
- catch (error) {
184
- if (error instanceof zod_1.ZodError) {
185
- return sendBadResponseError(response, error);
186
- }
187
- next(error);
188
- }
237
+ await validateAndSendResponse(response, controllerResponse, options.responses, debug);
189
238
  });
190
239
  }
191
240
  catch (error) {
192
- if (error instanceof zod_1.ZodError) {
193
- return sendBadRequestError(response, error);
194
- }
195
- if (isTypedError(error) &&
196
- options.errors &&
197
- options.errors[error.type]) {
198
- const statusCode = options.errors[error.type];
199
- return sendKnownErrorResponse(response, statusCode, error, debug);
200
- }
201
- if (error instanceof Error) {
202
- return response.status(500).send({
203
- error: http_1.HTTPStatus.INTERNAL_SERVER_ERROR,
204
- message: error.message,
205
- ...(debug && { stack: error.stack }),
206
- });
207
- }
208
- return response.status(500).send({
209
- error: http_1.HTTPStatus.INTERNAL_SERVER_ERROR,
210
- message: 'Unknown Error',
211
- });
241
+ sendErrorResponse(response, error, options.errors, debug);
212
242
  }
213
243
  });
214
244
  };
215
245
  exports.mountEndpoint = mountEndpoint;
216
- const registerEndpoint = (registry, method, prefixPath, path, summary,
217
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
218
- options, tags, debug) => {
219
- let bodySchema = options.schemas?.body || undefined;
220
- let paramsSchema = options.schemas?.params || undefined;
221
- let querySchema = options.schemas?.query || undefined;
222
- let headersSchema = options.schemas?.headers || undefined;
223
- let responseSchemas = options.responses;
224
- if (options.errors) {
225
- for (const errorType of Object.keys(options.errors)) {
226
- const statusCode = options.errors[errorType];
227
- responseSchemas.push({
228
- status: statusCode,
229
- description: errorType,
230
- schema: zod_1.z.object({
231
- error: zod_1.z.literal(errorType),
232
- message: zod_1.z.string(),
233
- }),
234
- });
235
- }
236
- }
237
- for (const middleware of options.middlewares || []) {
238
- if (middleware.options.schemas?.body) {
239
- bodySchema = bodySchema
240
- ? bodySchema.merge(middleware.options.schemas.body)
241
- : middleware.options.schemas.body;
242
- }
243
- if (middleware.options.schemas?.params) {
244
- paramsSchema = paramsSchema
245
- ? paramsSchema.merge(middleware.options.schemas.params)
246
- : middleware.options.schemas.params;
247
- }
248
- if (middleware.options.schemas?.query) {
249
- querySchema = querySchema
250
- ? querySchema.merge(middleware.options.schemas.query)
251
- : middleware.options.schemas.query;
252
- }
253
- if (middleware.options.schemas?.headers) {
254
- headersSchema = headersSchema
255
- ? headersSchema.merge(middleware.options.schemas.headers)
256
- : middleware.options.schemas.headers;
257
- }
258
- if (middleware.options.responses) {
259
- responseSchemas = responseSchemas.concat(middleware.options.responses);
260
- }
261
- if (middleware.options.errors) {
262
- for (const errorType of Object.keys(middleware.options.errors)) {
263
- const statusCode = middleware.options.errors[errorType];
264
- responseSchemas.push({
265
- status: statusCode,
266
- description: errorType,
267
- schema: zod_1.z.object({
268
- error: zod_1.z.literal(errorType),
269
- message: zod_1.z.string(),
270
- }),
271
- });
272
- }
273
- }
274
- }
275
- // Add ZodError response
276
- responseSchemas.push({
277
- status: 400,
278
- description: 'Bad Request',
279
- schema: zod_1.z.object({
280
- error: zod_1.z.literal(http_1.HTTPStatus.BAD_REQUEST),
281
- message: zod_1.z.string(),
282
- issues: zod_1.z.array(zod_1.z
283
- .object({
284
- message: zod_1.z.string(),
285
- })
286
- .openapi({ additionalProperties: true })),
287
- }),
288
- });
289
- // Add UnknownError response
290
- responseSchemas.push({
291
- status: 500,
292
- description: 'Internal Server Error',
293
- schema: zod_1.z.object({
294
- error: zod_1.z.literal(http_1.HTTPStatus.INTERNAL_SERVER_ERROR),
295
- message: zod_1.z.string(),
296
- ...(debug && { stack: zod_1.z.string().optional() }),
297
- }),
298
- });
299
- const responseMap = buildResponseConfig(responseSchemas);
300
- registry.registerPath({
301
- method,
302
- path: mapExpressParamsToOpenAPIParams(prefixPath + path),
303
- summary,
304
- tags,
305
- // description: summary,
306
- request: {
307
- ...(bodySchema && {
308
- body: {
309
- content: {
310
- 'application/json': {
311
- schema: bodySchema,
312
- },
313
- },
314
- },
315
- }),
316
- params: paramsSchema,
317
- query: querySchema,
318
- headers: headersSchema ? [headersSchema] : [],
319
- },
320
- // tags: ['meep'],
321
- responses: responseMap,
322
- });
323
- };
324
- exports.registerEndpoint = registerEndpoint;
@@ -0,0 +1,5 @@
1
+ import { OpenAPIRegistry } from '@asteasolutions/zod-to-openapi';
2
+ import { EndpointOptions } from './types/endpoint';
3
+ type HttpMethod = 'get' | 'post' | 'put' | 'delete' | 'patch';
4
+ export declare const registerEndpoint: (registry: OpenAPIRegistry, method: HttpMethod, prefixPath: string, path: string, summary: string, options: EndpointOptions<any, any, any, any, any, any>, tags: string[], debug: boolean) => void;
5
+ export {};
@@ -0,0 +1,167 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.registerEndpoint = void 0;
4
+ const zod_1 = require("zod");
5
+ const http_1 = require("./types/http");
6
+ const mapExpressParamsToOpenAPIParams = (path) => path.replaceAll(/\:([^\:\/]+)/g, '{$1}');
7
+ const buildResponseConfig = (responseOptions) => {
8
+ const responses = {};
9
+ responseOptions.forEach(response => {
10
+ const isAlreadyPresent = !!responses[String(response.status)];
11
+ if (!isAlreadyPresent) {
12
+ responses[String(response.status)] = {
13
+ description: response.description,
14
+ ...(!(response.schema instanceof zod_1.z.ZodVoid) && {
15
+ content: {
16
+ 'application/json': {
17
+ schema: response.schema,
18
+ },
19
+ },
20
+ }),
21
+ };
22
+ return;
23
+ }
24
+ const existingResponse = responses[response.status];
25
+ existingResponse.description =
26
+ existingResponse.description + ' | ' + response.description;
27
+ // No Content merge
28
+ if (!response.schema) {
29
+ return;
30
+ }
31
+ // No Content
32
+ if (!existingResponse.content ||
33
+ !existingResponse.content['application/json']) {
34
+ return;
35
+ }
36
+ existingResponse.content['application/json'].schema =
37
+ existingResponse.content['application/json'].schema.or(response.schema);
38
+ });
39
+ return responses;
40
+ };
41
+ const registerEndpoint = (registry, method, prefixPath, path, summary,
42
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
43
+ options, tags, debug) => {
44
+ let bodySchema = options.schemas?.body || undefined;
45
+ let paramsSchema = options.schemas?.params || undefined;
46
+ let querySchema = options.schemas?.query || undefined;
47
+ let headersSchema = options.schemas?.headers || undefined;
48
+ let responseSchemas = options.responses;
49
+ if (options.errors) {
50
+ for (const errorType of Object.keys(options.errors)) {
51
+ const statusCode = options.errors[errorType];
52
+ responseSchemas.push({
53
+ status: statusCode,
54
+ description: errorType,
55
+ schema: zod_1.z.object({
56
+ error: zod_1.z.literal(errorType),
57
+ message: zod_1.z.string(),
58
+ }),
59
+ });
60
+ }
61
+ }
62
+ for (const middleware of options.middlewares || []) {
63
+ if (middleware.options.schemas?.body) {
64
+ bodySchema = bodySchema
65
+ ? bodySchema.merge(middleware.options.schemas.body)
66
+ : middleware.options.schemas.body;
67
+ }
68
+ if (middleware.options.schemas?.params) {
69
+ paramsSchema = paramsSchema
70
+ ? paramsSchema.merge(middleware.options.schemas.params)
71
+ : middleware.options.schemas.params;
72
+ }
73
+ if (middleware.options.schemas?.query) {
74
+ querySchema = querySchema
75
+ ? querySchema.merge(middleware.options.schemas.query)
76
+ : middleware.options.schemas.query;
77
+ }
78
+ if (middleware.options.schemas?.headers) {
79
+ headersSchema = headersSchema
80
+ ? headersSchema.merge(middleware.options.schemas.headers)
81
+ : middleware.options.schemas.headers;
82
+ }
83
+ if (middleware.options.responses) {
84
+ responseSchemas = responseSchemas.concat(middleware.options.responses);
85
+ }
86
+ if (middleware.options.errors) {
87
+ for (const errorType of Object.keys(middleware.options.errors)) {
88
+ const statusCode = middleware.options.errors[errorType];
89
+ responseSchemas.push({
90
+ status: statusCode,
91
+ description: errorType,
92
+ schema: zod_1.z.object({
93
+ error: zod_1.z.literal(errorType),
94
+ message: zod_1.z.string(),
95
+ ...(debug && {
96
+ debug: zod_1.z
97
+ .object({
98
+ name: zod_1.z.string().optional(),
99
+ stack: zod_1.z.string().optional(),
100
+ message: zod_1.z.string().optional(),
101
+ meta: zod_1.z.object({}).passthrough().optional(),
102
+ })
103
+ .optional(),
104
+ }),
105
+ }),
106
+ });
107
+ }
108
+ }
109
+ }
110
+ // Add ZodError response
111
+ responseSchemas.push({
112
+ status: 400,
113
+ description: http_1.HTTPStatus.BAD_REQUEST,
114
+ schema: zod_1.z.object({
115
+ error: zod_1.z.literal(http_1.HTTPStatus.BAD_REQUEST),
116
+ message: zod_1.z.string(),
117
+ issues: zod_1.z.array(zod_1.z
118
+ .object({
119
+ message: zod_1.z.string(),
120
+ })
121
+ .openapi({ additionalProperties: true })),
122
+ }),
123
+ });
124
+ // Add UnknownError response
125
+ responseSchemas.push({
126
+ status: 500,
127
+ description: http_1.HTTPStatus.INTERNAL_SERVER_ERROR,
128
+ schema: zod_1.z.object({
129
+ error: zod_1.z.literal(http_1.HTTPStatus.INTERNAL_SERVER_ERROR),
130
+ message: zod_1.z.string(),
131
+ ...(debug && {
132
+ debug: zod_1.z
133
+ .object({
134
+ name: zod_1.z.string().optional(),
135
+ stack: zod_1.z.string().optional(),
136
+ message: zod_1.z.string().optional(),
137
+ meta: zod_1.z.object({}).passthrough().optional(),
138
+ })
139
+ .optional(),
140
+ }),
141
+ }),
142
+ });
143
+ const responseMap = buildResponseConfig(responseSchemas);
144
+ registry.registerPath({
145
+ method,
146
+ path: mapExpressParamsToOpenAPIParams(prefixPath + path),
147
+ summary,
148
+ tags,
149
+ // description: summary,
150
+ request: {
151
+ ...(bodySchema && {
152
+ body: {
153
+ content: {
154
+ 'application/json': {
155
+ schema: bodySchema,
156
+ },
157
+ },
158
+ },
159
+ }),
160
+ params: paramsSchema,
161
+ query: querySchema,
162
+ headers: headersSchema ? [headersSchema] : [],
163
+ },
164
+ responses: responseMap,
165
+ });
166
+ };
167
+ exports.registerEndpoint = registerEndpoint;
@@ -147,7 +147,7 @@ class ServiceIdentity {
147
147
  if (method !== payload.method) {
148
148
  return respond((0, api_1.forbidden)(`${method} !== ${payload.method}`));
149
149
  }
150
- next({ payload: payload.data });
150
+ return next({ payload: payload.data });
151
151
  });
152
152
  this.identityJWKSRoute = async (_, response) => {
153
153
  response.send(await this.getIdentityJWKS());
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lucaapp/service-utils",
3
- "version": "1.40.3",
3
+ "version": "1.41.0",
4
4
  "main": "dist/index.js",
5
5
  "types": "dist/index.d.ts",
6
6
  "files": [