@lucaapp/service-utils 1.22.0 → 1.23.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/dist/index.d.ts CHANGED
@@ -1,3 +1,4 @@
1
+ export * from './lib/api';
1
2
  export * from './lib/kafka';
2
3
  export * from './lib/serviceIdentity';
3
4
  export * from './lib/urlEncoded';
package/dist/index.js CHANGED
@@ -14,6 +14,7 @@ var __exportStar = (this && this.__exportStar) || function(m, exports) {
14
14
  for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
15
15
  };
16
16
  Object.defineProperty(exports, "__esModule", { value: true });
17
+ __exportStar(require("./lib/api"), exports);
17
18
  __exportStar(require("./lib/kafka"), exports);
18
19
  __exportStar(require("./lib/serviceIdentity"), exports);
19
20
  __exportStar(require("./lib/urlEncoded"), exports);
@@ -0,0 +1,29 @@
1
+ import { Router } from 'express';
2
+ import 'express-async-errors';
3
+ import { OpenAPIRegistry } from '@asteasolutions/zod-to-openapi';
4
+ import { EndpointHandler, EndpointOptions } from './types/endpoint';
5
+ import { Middleware, EndpointResponseSchema } from './types/middleware';
6
+ type OpenApiVersion = '3.0.0' | '3.0.1' | '3.0.2' | '3.0.3' | '3.1.0';
7
+ type ApiConstructorOptions = {
8
+ prefixPath?: string;
9
+ openApiVersion?: OpenApiVersion;
10
+ apiVersion?: string;
11
+ apiTitle?: string;
12
+ apiDescription?: string;
13
+ };
14
+ export declare class Api {
15
+ router: Router;
16
+ registry: OpenAPIRegistry;
17
+ openApiVersion: OpenApiVersion;
18
+ apiVersion: string;
19
+ apiTitle: string;
20
+ apiDescription: string;
21
+ private prefixPath;
22
+ constructor(options?: ApiConstructorOptions);
23
+ get<TResponseSchemas extends ReadonlyArray<EndpointResponseSchema>, TRequestBody = undefined, TRequestParams = undefined, TRequestQuery = undefined, TRequestHeaders = undefined, TMiddlewares extends ReadonlyArray<Middleware<any, any, any, any, any, any>> | undefined = undefined>(path: string, summary: string, options: EndpointOptions<TResponseSchemas, TRequestBody, TRequestParams, TRequestQuery, TRequestHeaders, TMiddlewares>, handler: EndpointHandler<TResponseSchemas, TRequestBody, TRequestParams, TRequestQuery, TRequestHeaders, TMiddlewares>): void;
24
+ post<TResponseSchemas extends ReadonlyArray<EndpointResponseSchema>, TRequestBody = undefined, TRequestParams = undefined, TRequestQuery = undefined, TRequestHeaders = undefined, TMiddlewares extends ReadonlyArray<Middleware<any, any, any, any, any, any>> | undefined = undefined>(path: string, summary: string, options: EndpointOptions<TResponseSchemas, TRequestBody, TRequestParams, TRequestQuery, TRequestHeaders, TMiddlewares>, handler: EndpointHandler<TResponseSchemas, TRequestBody, TRequestParams, TRequestQuery, TRequestHeaders, TMiddlewares>): void;
25
+ patch<TResponseSchemas extends ReadonlyArray<EndpointResponseSchema>, TRequestBody = undefined, TRequestParams = undefined, TRequestQuery = undefined, TRequestHeaders = undefined, TMiddlewares extends ReadonlyArray<Middleware<any, any, any, any, any, any>> | undefined = undefined>(path: string, summary: string, options: EndpointOptions<TResponseSchemas, TRequestBody, TRequestParams, TRequestQuery, TRequestHeaders, TMiddlewares>, handler: EndpointHandler<TResponseSchemas, TRequestBody, TRequestParams, TRequestQuery, TRequestHeaders, TMiddlewares>): void;
26
+ generateOpenAPISpec(): import("openapi3-ts").OpenAPIObject;
27
+ mountSwaggerMiddlewares(): void;
28
+ }
29
+ export {};
@@ -0,0 +1,58 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.Api = void 0;
7
+ const express_1 = require("express");
8
+ require("express-async-errors");
9
+ const swagger_ui_express_1 = __importDefault(require("swagger-ui-express"));
10
+ const zod_to_openapi_1 = require("@asteasolutions/zod-to-openapi");
11
+ const endpoint_1 = require("./endpoint");
12
+ class Api {
13
+ constructor(options = {}) {
14
+ this.router = (0, express_1.Router)();
15
+ this.registry = new zod_to_openapi_1.OpenAPIRegistry();
16
+ this.prefixPath = options.prefixPath || '';
17
+ this.openApiVersion = options.openApiVersion || '3.0.0';
18
+ this.apiVersion = options.apiVersion || '1.0.0';
19
+ this.apiTitle = options.apiTitle || 'API';
20
+ this.apiDescription = options.apiDescription || '';
21
+ this.mountSwaggerMiddlewares();
22
+ }
23
+ get(path, summary, options, handler) {
24
+ (0, endpoint_1.mountEndpoint)(this.router, 'get', path, options, handler);
25
+ (0, endpoint_1.registerEndpoint)(this.registry, 'get', this.prefixPath, path, summary, options);
26
+ }
27
+ post(path, summary, options, handler) {
28
+ (0, endpoint_1.mountEndpoint)(this.router, 'post', path, options, handler);
29
+ (0, endpoint_1.registerEndpoint)(this.registry, 'post', this.prefixPath, path, summary, options);
30
+ }
31
+ patch(path, summary, options, handler) {
32
+ (0, endpoint_1.mountEndpoint)(this.router, 'patch', path, options, handler);
33
+ (0, endpoint_1.registerEndpoint)(this.registry, 'patch', this.prefixPath, path, summary, options);
34
+ }
35
+ generateOpenAPISpec() {
36
+ const generator = new zod_to_openapi_1.OpenAPIGenerator(this.registry.definitions, this.openApiVersion);
37
+ const spec = generator.generateDocument({
38
+ info: {
39
+ version: this.apiVersion,
40
+ title: this.apiTitle,
41
+ description: this.apiDescription,
42
+ },
43
+ });
44
+ return spec;
45
+ }
46
+ mountSwaggerMiddlewares() {
47
+ this.router.use('/swagger', swagger_ui_express_1.default.serve);
48
+ this.router.get('/swagger', (request, response, next) => {
49
+ const spec = this.generateOpenAPISpec();
50
+ swagger_ui_express_1.default.setup(spec)(request, response, next);
51
+ });
52
+ this.router.get('/swagger.json', (request, response) => {
53
+ const spec = this.generateOpenAPISpec();
54
+ response.send(spec);
55
+ });
56
+ }
57
+ }
58
+ exports.Api = Api;
@@ -0,0 +1,8 @@
1
+ import { Router } from 'express';
2
+ import { OpenAPIRegistry } from '@asteasolutions/zod-to-openapi';
3
+ import { Middleware, EndpointResponseSchema } from './types/middleware';
4
+ import { EndpointOptions, EndpointHandler } from './types/endpoint';
5
+ type HttpMethod = 'get' | 'post' | 'put' | 'delete' | 'patch';
6
+ export declare const mountEndpoint: <TResponseSchemas extends readonly EndpointResponseSchema[], TRequestBody = undefined, TRequestParams = undefined, TRequestQuery = undefined, TRequestHeaders = undefined, TMiddlewares extends readonly Middleware<any, any, any, any, any, any>[] | undefined = undefined>(router: Router, method: HttpMethod, path: string, options: EndpointOptions<TResponseSchemas, TRequestBody, TRequestParams, TRequestQuery, TRequestHeaders, TMiddlewares>, handler: EndpointHandler<TResponseSchemas, TRequestBody, TRequestParams, TRequestQuery, TRequestHeaders, TMiddlewares>) => void;
7
+ export declare const registerEndpoint: (registry: OpenAPIRegistry, method: HttpMethod, prefixPath: string, path: string, summary: string, options: EndpointOptions<any, any, any, any, any, any>) => void;
8
+ export {};
@@ -0,0 +1,115 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.registerEndpoint = exports.mountEndpoint = void 0;
7
+ const zod_1 = require("zod");
8
+ const assert_1 = __importDefault(require("assert"));
9
+ const buildResponseConfig = (responseOptions) => {
10
+ const responses = {};
11
+ responseOptions.forEach(response => {
12
+ responses[String(response.status)] = {
13
+ description: response.description,
14
+ ...(!(response.schema instanceof zod_1.ZodVoid) && {
15
+ content: {
16
+ 'application/json': {
17
+ schema: response.schema,
18
+ },
19
+ },
20
+ }),
21
+ };
22
+ });
23
+ return responses;
24
+ };
25
+ const mountEndpoint = (router, method, path, options, handler) => {
26
+ (0, assert_1.default)(options.responses.length > 0, 'You need to specify at least one response');
27
+ router[method](path, async (request, response) => {
28
+ // parse request schemas
29
+ const handlerRequestBody = ((await options.schemas?.body?.parseAsync(request.body)) || undefined);
30
+ const handlerRequestParams = ((await options.schemas?.params?.parseAsync(request.params)) || undefined);
31
+ const handlerRequestQuery = ((await options.schemas?.query?.parseAsync(request.query)) || undefined);
32
+ const handlerRequestHeaders = ((await options.schemas?.headers?.parseAsync(request.headers)) || undefined);
33
+ const handlerRequest = {
34
+ body: handlerRequestBody,
35
+ params: handlerRequestParams,
36
+ query: handlerRequestQuery,
37
+ headers: handlerRequestHeaders,
38
+ };
39
+ // TODO: handle middlewares and build context object
40
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
41
+ const ctx = {};
42
+ await handler(handlerRequest, ctx, controllerResponse => {
43
+ // this assertion is necessary due to the type possibly being unknown because of the edge case of an schema array with 0 elements
44
+ (0, assert_1.default)(typeof controllerResponse.status === 'number');
45
+ // get schema respective to status code
46
+ const { schema } = options.responses.find(responseSchema => responseSchema.status === controllerResponse.status);
47
+ // validate response schema
48
+ const validatedResponseBody = schema.parse(controllerResponse.body);
49
+ if (validatedResponseBody === undefined) {
50
+ // No Content Response
51
+ return response.status(controllerResponse.status).end();
52
+ }
53
+ response.status(controllerResponse.status).send(validatedResponseBody);
54
+ });
55
+ });
56
+ };
57
+ exports.mountEndpoint = mountEndpoint;
58
+ const registerEndpoint = (registry, method, prefixPath, path, summary,
59
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
60
+ options) => {
61
+ let bodySchema = options.schemas?.body || undefined;
62
+ let paramsSchema = options.schemas?.params || undefined;
63
+ let querySchema = options.schemas?.query || undefined;
64
+ let headersSchema = options.schemas?.headers || undefined;
65
+ let responseSchemas = options.responses;
66
+ for (const middleware of options.middlewares || []) {
67
+ if (middleware.options.schemas?.body) {
68
+ bodySchema = bodySchema
69
+ ? bodySchema.merge(middleware.options.schemas.body)
70
+ : middleware.options.schemas.body;
71
+ }
72
+ if (middleware.options.schemas?.params) {
73
+ paramsSchema = paramsSchema
74
+ ? paramsSchema.merge(middleware.options.schemas.params)
75
+ : middleware.options.schemas.params;
76
+ }
77
+ if (middleware.options.schemas?.query) {
78
+ querySchema = querySchema
79
+ ? querySchema.merge(middleware.options.schemas.query)
80
+ : middleware.options.schemas.query;
81
+ }
82
+ if (middleware.options.schemas?.headers) {
83
+ headersSchema = headersSchema
84
+ ? headersSchema.merge(middleware.options.schemas.headers)
85
+ : middleware.options.schemas.headers;
86
+ }
87
+ if (middleware.options.responses) {
88
+ responseSchemas = responseSchemas.concat(middleware.options.responses);
89
+ }
90
+ }
91
+ const responseMap = buildResponseConfig(responseSchemas);
92
+ registry.registerPath({
93
+ method,
94
+ path: prefixPath + path,
95
+ summary,
96
+ // description: summary,
97
+ request: {
98
+ ...(bodySchema && {
99
+ body: {
100
+ content: {
101
+ 'application/json': {
102
+ schema: bodySchema,
103
+ },
104
+ },
105
+ },
106
+ }),
107
+ params: paramsSchema,
108
+ query: querySchema,
109
+ headers: headersSchema ? [headersSchema] : [],
110
+ },
111
+ // tags: ['meep'],
112
+ responses: responseMap,
113
+ });
114
+ };
115
+ exports.registerEndpoint = registerEndpoint;
@@ -0,0 +1,6 @@
1
+ export { extendZodWithOpenApi } from '@asteasolutions/zod-to-openapi';
2
+ export * from './api';
3
+ export * from './endpoint';
4
+ export * from './middleware';
5
+ export * from './response';
6
+ export * from './send';
@@ -0,0 +1,24 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __exportStar = (this && this.__exportStar) || function(m, exports) {
14
+ for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
15
+ };
16
+ Object.defineProperty(exports, "__esModule", { value: true });
17
+ exports.extendZodWithOpenApi = void 0;
18
+ var zod_to_openapi_1 = require("@asteasolutions/zod-to-openapi");
19
+ Object.defineProperty(exports, "extendZodWithOpenApi", { enumerable: true, get: function () { return zod_to_openapi_1.extendZodWithOpenApi; } });
20
+ __exportStar(require("./api"), exports);
21
+ __exportStar(require("./endpoint"), exports);
22
+ __exportStar(require("./middleware"), exports);
23
+ __exportStar(require("./response"), exports);
24
+ __exportStar(require("./send"), exports);
@@ -0,0 +1,2 @@
1
+ import { Middleware, MiddlewareOptions, MiddlewareHandler, EndpointResponseSchema } from './types/middleware';
2
+ export declare const createMiddleware: <TResponseSchemas extends readonly EndpointResponseSchema[], TRequestBody = undefined, TRequestParams = undefined, TRequestQuery = undefined, TRequestHeaders = undefined, TContext = undefined>(options: MiddlewareOptions<TResponseSchemas, TRequestBody, TRequestParams, TRequestQuery, TRequestHeaders, TContext>, handler: MiddlewareHandler<TResponseSchemas, TRequestBody, TRequestParams, TRequestQuery, TRequestHeaders, TContext>) => Middleware<TResponseSchemas, TRequestBody, TRequestParams, TRequestQuery, TRequestHeaders, TContext>;
@@ -0,0 +1,10 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.createMiddleware = void 0;
4
+ const createMiddleware = (options, handler) => {
5
+ return {
6
+ options,
7
+ handler,
8
+ };
9
+ };
10
+ exports.createMiddleware = createMiddleware;
@@ -0,0 +1,85 @@
1
+ import { z } from 'zod';
2
+ declare const noContentSchema: z.ZodVoid;
3
+ declare const badRequestErrorSchema: z.ZodObject<{
4
+ statusCode: z.ZodLiteral<400>;
5
+ error: z.ZodString;
6
+ message: z.ZodString;
7
+ }, "strip", z.ZodTypeAny, {
8
+ error: string;
9
+ message: string;
10
+ statusCode: 400;
11
+ }, {
12
+ error: string;
13
+ message: string;
14
+ statusCode: 400;
15
+ }>;
16
+ declare const unauthorizedErrorSchema: z.ZodObject<{
17
+ statusCode: z.ZodLiteral<401>;
18
+ error: z.ZodString;
19
+ message: z.ZodString;
20
+ }, "strip", z.ZodTypeAny, {
21
+ error: string;
22
+ message: string;
23
+ statusCode: 401;
24
+ }, {
25
+ error: string;
26
+ message: string;
27
+ statusCode: 401;
28
+ }>;
29
+ declare const forbiddenErrorSchema: z.ZodObject<{
30
+ statusCode: z.ZodLiteral<403>;
31
+ error: z.ZodString;
32
+ message: z.ZodString;
33
+ }, "strip", z.ZodTypeAny, {
34
+ error: string;
35
+ message: string;
36
+ statusCode: 403;
37
+ }, {
38
+ error: string;
39
+ message: string;
40
+ statusCode: 403;
41
+ }>;
42
+ declare const notFoundErrorSchema: z.ZodObject<{
43
+ statusCode: z.ZodLiteral<404>;
44
+ error: z.ZodString;
45
+ message: z.ZodString;
46
+ }, "strip", z.ZodTypeAny, {
47
+ error: string;
48
+ message: string;
49
+ statusCode: 404;
50
+ }, {
51
+ error: string;
52
+ message: string;
53
+ statusCode: 404;
54
+ }>;
55
+ export declare const okResponse: <T>(schema: z.ZodType<T, z.ZodTypeDef, T>) => {
56
+ status: 200;
57
+ description: string;
58
+ schema: z.ZodType<T, z.ZodTypeDef, T>;
59
+ };
60
+ export declare const noContentResponse: () => {
61
+ status: 204;
62
+ description: string;
63
+ schema: typeof noContentSchema;
64
+ };
65
+ export declare const badRequestResponse: () => {
66
+ status: 400;
67
+ description: string;
68
+ schema: typeof badRequestErrorSchema;
69
+ };
70
+ export declare const unauthorizedResponse: () => {
71
+ status: 401;
72
+ description: string;
73
+ schema: typeof unauthorizedErrorSchema;
74
+ };
75
+ export declare const forbiddenResponse: () => {
76
+ status: 403;
77
+ description: string;
78
+ schema: typeof forbiddenErrorSchema;
79
+ };
80
+ export declare const notFoundResponse: () => {
81
+ status: 404;
82
+ description: string;
83
+ schema: typeof notFoundErrorSchema;
84
+ };
85
+ export {};
@@ -0,0 +1,65 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.notFoundResponse = exports.forbiddenResponse = exports.unauthorizedResponse = exports.badRequestResponse = exports.noContentResponse = exports.okResponse = void 0;
4
+ const zod_to_openapi_1 = require("@asteasolutions/zod-to-openapi");
5
+ const zod_1 = require("zod");
6
+ (0, zod_to_openapi_1.extendZodWithOpenApi)(zod_1.z);
7
+ // response schemas
8
+ const noContentSchema = zod_1.z.void();
9
+ const badRequestErrorSchema = zod_1.z.object({
10
+ statusCode: zod_1.z.literal(400),
11
+ error: zod_1.z.string(),
12
+ message: zod_1.z.string(),
13
+ });
14
+ const unauthorizedErrorSchema = zod_1.z.object({
15
+ statusCode: zod_1.z.literal(401),
16
+ error: zod_1.z.string(),
17
+ message: zod_1.z.string(),
18
+ });
19
+ const forbiddenErrorSchema = zod_1.z.object({
20
+ statusCode: zod_1.z.literal(403),
21
+ error: zod_1.z.string(),
22
+ message: zod_1.z.string(),
23
+ });
24
+ const notFoundErrorSchema = zod_1.z.object({
25
+ statusCode: zod_1.z.literal(404),
26
+ error: zod_1.z.string(),
27
+ message: zod_1.z.string(),
28
+ });
29
+ // response functions
30
+ const okResponse = (schema) => ({
31
+ status: 200,
32
+ description: 'OK',
33
+ schema,
34
+ });
35
+ exports.okResponse = okResponse;
36
+ const noContentResponse = () => ({
37
+ status: 204,
38
+ description: 'No Content',
39
+ schema: noContentSchema,
40
+ });
41
+ exports.noContentResponse = noContentResponse;
42
+ const badRequestResponse = () => ({
43
+ status: 400,
44
+ description: 'Bad Request',
45
+ schema: badRequestErrorSchema,
46
+ });
47
+ exports.badRequestResponse = badRequestResponse;
48
+ const unauthorizedResponse = () => ({
49
+ status: 401,
50
+ description: 'Unauthorized',
51
+ schema: unauthorizedErrorSchema,
52
+ });
53
+ exports.unauthorizedResponse = unauthorizedResponse;
54
+ const forbiddenResponse = () => ({
55
+ status: 403,
56
+ description: 'Forbidden',
57
+ schema: forbiddenErrorSchema,
58
+ });
59
+ exports.forbiddenResponse = forbiddenResponse;
60
+ const notFoundResponse = () => ({
61
+ status: 404,
62
+ description: 'Not Found',
63
+ schema: notFoundErrorSchema,
64
+ });
65
+ exports.notFoundResponse = notFoundResponse;
@@ -0,0 +1,40 @@
1
+ export declare const ok: <T>(body: T) => {
2
+ status: 200;
3
+ body: T;
4
+ };
5
+ export declare const noContent: () => {
6
+ status: 204;
7
+ body: void;
8
+ };
9
+ export declare const badRequest: (message?: string) => {
10
+ status: 400;
11
+ body: {
12
+ statusCode: 400;
13
+ error: string;
14
+ message: string;
15
+ };
16
+ };
17
+ export declare const unauthorized: (message?: string) => {
18
+ status: 401;
19
+ body: {
20
+ statusCode: 401;
21
+ error: string;
22
+ message: string;
23
+ };
24
+ };
25
+ export declare const forbidden: (message?: string) => {
26
+ status: 403;
27
+ body: {
28
+ statusCode: 403;
29
+ error: string;
30
+ message: string;
31
+ };
32
+ };
33
+ export declare const notFound: (message?: string) => {
34
+ status: 404;
35
+ body: {
36
+ statusCode: 404;
37
+ error: string;
38
+ message: string;
39
+ };
40
+ };
@@ -0,0 +1,49 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.notFound = exports.forbidden = exports.unauthorized = exports.badRequest = exports.noContent = exports.ok = void 0;
4
+ const ok = (body) => ({
5
+ status: 200,
6
+ body,
7
+ });
8
+ exports.ok = ok;
9
+ const noContent = () => ({
10
+ status: 204,
11
+ body: undefined,
12
+ });
13
+ exports.noContent = noContent;
14
+ const badRequest = (message) => ({
15
+ status: 400,
16
+ body: {
17
+ statusCode: 400,
18
+ error: 'Bad request',
19
+ message: message || 'Bad request',
20
+ },
21
+ });
22
+ exports.badRequest = badRequest;
23
+ const unauthorized = (message) => ({
24
+ status: 401,
25
+ body: {
26
+ statusCode: 401,
27
+ error: 'Unauthorized',
28
+ message: message || 'Unauthorized',
29
+ },
30
+ });
31
+ exports.unauthorized = unauthorized;
32
+ const forbidden = (message) => ({
33
+ status: 403,
34
+ body: {
35
+ statusCode: 403,
36
+ error: 'Forbidden',
37
+ message: message || 'Forbidden',
38
+ },
39
+ });
40
+ exports.forbidden = forbidden;
41
+ const notFound = (message) => ({
42
+ status: 404,
43
+ body: {
44
+ statusCode: 404,
45
+ error: 'Not found',
46
+ message: message || 'Not found',
47
+ },
48
+ });
49
+ exports.notFound = notFound;
@@ -0,0 +1,19 @@
1
+ import { z } from 'zod';
2
+ import { Middleware, EndpointResponseSchema } from './middleware';
3
+ import { ArrayToUnion, ExtractZodOutput, ExtractZodOutputFromMiddleware, Always, Merge } from './utils';
4
+ export type EndpointHandler<TResponse, TRequestBody = undefined, TRequestParams = undefined, TRequestQuery = undefined, TRequestHeaders = undefined, TMiddlewares = undefined> = (request: {
5
+ body: TRequestBody;
6
+ params: TRequestParams;
7
+ query: TRequestQuery;
8
+ headers: TRequestHeaders;
9
+ }, context: Merge<Always<ExtractZodOutputFromMiddleware<ArrayToUnion<TMiddlewares>>>>, send: (response: ExtractZodOutput<ArrayToUnion<TResponse>>) => void) => Promise<void>;
10
+ export type EndpointOptions<TResponseSchemas extends ReadonlyArray<EndpointResponseSchema>, TRequestBody = undefined, TRequestParams = undefined, TRequestQuery = undefined, TRequestHeaders = undefined, TMiddlewares extends ReadonlyArray<Middleware<any, any, any, any, any, any>> | undefined = undefined> = {
11
+ schemas?: {
12
+ body?: z.ZodObject<z.ZodRawShape, 'strip', z.ZodTypeAny, TRequestBody>;
13
+ params?: z.ZodObject<z.ZodRawShape, 'strip', z.ZodTypeAny, TRequestParams>;
14
+ query?: z.ZodObject<z.ZodRawShape, 'strip', z.ZodTypeAny, TRequestQuery>;
15
+ headers?: z.ZodObject<z.ZodRawShape, 'strip', z.ZodTypeAny, TRequestHeaders>;
16
+ };
17
+ middlewares?: TMiddlewares;
18
+ responses: TResponseSchemas;
19
+ };
@@ -0,0 +1,2 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
@@ -0,0 +1,27 @@
1
+ import { z } from 'zod';
2
+ import { ArrayToUnion, ExtractZodOutput } from './utils';
3
+ export type EndpointResponseSchema = {
4
+ status: number;
5
+ description: string;
6
+ schema: z.ZodSchema<any> | z.ZodVoid;
7
+ };
8
+ export type MiddlewareHandler<TResponseSchema, TRequestBody = undefined, TRequestParams = undefined, TRequestQuery = undefined, TRequestHeaders = undefined, TContext = undefined> = (request: {
9
+ body: TRequestBody;
10
+ params: TRequestParams;
11
+ query: TRequestQuery;
12
+ headers: TRequestHeaders;
13
+ }, send: (response: ExtractZodOutput<ArrayToUnion<TResponseSchema>>) => void, next: (context: TContext) => void) => Promise<void>;
14
+ export type MiddlewareOptions<TResponseSchemas extends ReadonlyArray<EndpointResponseSchema>, TRequestBody = undefined, TRequestParams = undefined, TRequestQuery = undefined, TRequestHeaders = undefined, TContext = undefined> = {
15
+ schemas?: {
16
+ body?: z.ZodSchema<TRequestBody>;
17
+ params?: z.ZodObject<z.ZodRawShape, 'strip', z.ZodTypeAny, TRequestParams>;
18
+ query?: z.ZodObject<z.ZodRawShape, 'strip', z.ZodTypeAny, TRequestQuery>;
19
+ headers?: z.ZodObject<z.ZodRawShape, 'strip', z.ZodTypeAny, TRequestHeaders>;
20
+ context?: z.ZodSchema<TContext>;
21
+ };
22
+ responses: TResponseSchemas;
23
+ };
24
+ export type Middleware<TResponseSchemas extends ReadonlyArray<EndpointResponseSchema>, TRequestBody = undefined, TRequestParams = undefined, TRequestQuery = undefined, TRequestHeaders = undefined, TContext = undefined> = {
25
+ options: MiddlewareOptions<TResponseSchemas, TRequestBody, TRequestParams, TRequestQuery, TRequestHeaders, TContext>;
26
+ handler: MiddlewareHandler<TResponseSchemas, TRequestBody, TRequestParams, TRequestQuery, TRequestHeaders, TContext>;
27
+ };
@@ -0,0 +1,2 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
@@ -0,0 +1,25 @@
1
+ import { z } from 'zod';
2
+ export type ArrayToUnion<T> = T[keyof T];
3
+ export type ExtractZodOutput<T> = T extends {
4
+ status: infer Z;
5
+ schema: z.ZodSchema<infer V>;
6
+ } ? {
7
+ status: Z;
8
+ body: V;
9
+ } : never;
10
+ export type ExtractZodOutputFromMiddleware<T> = T extends {
11
+ options: {
12
+ schemas?: {
13
+ context?: z.ZodSchema<infer V>;
14
+ };
15
+ };
16
+ } ? V : undefined;
17
+ export type Always<T> = T extends object ? T : {};
18
+ type AllKeys<T> = T extends any ? keyof T : never;
19
+ type PickType<T, K extends AllKeys<T>> = T extends {
20
+ [k in K]: any;
21
+ } ? T[K] : never;
22
+ export type Merge<T extends object> = {
23
+ [k in AllKeys<T>]: PickType<T, k>;
24
+ };
25
+ export {};
@@ -0,0 +1,2 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
@@ -1,6 +1,6 @@
1
1
  type Location = {
2
2
  uuid: string;
3
- groupId: string;
3
+ groupId?: string;
4
4
  createdAt: Date;
5
5
  updatedAt: Date;
6
6
  };
@@ -1,3 +1,3 @@
1
1
  export { KafkaClient } from './kafkaClient';
2
2
  export { KafkaTopic } from './events';
3
- export type { KafkaEvent, EventPayloadHandler, KafkaConfiguration } from './types';
3
+ export type { KafkaEvent, EventPayloadHandler, KafkaConfiguration, } from './types';
@@ -17,4 +17,4 @@ type KafkaConfiguration = {
17
17
  type EventPayloadHandler<T extends KafkaTopic> = (message: Omit<KafkaMessage, 'value'> & {
18
18
  value: KafkaEvent<T>;
19
19
  }) => Promise<void>;
20
- export type { KafkaEvent, KafkaConfiguration, EventPayloadHandler, };
20
+ export type { KafkaEvent, KafkaConfiguration, EventPayloadHandler };
@@ -7,7 +7,9 @@ const generateSubscriptionId = () => {
7
7
  return (0, crypto_1.randomBytes)(32).toString('base64');
8
8
  };
9
9
  exports.generateSubscriptionId = generateSubscriptionId;
10
- const emitWebsocketEvent = async (kafkaClient, subId, type, data) => {
10
+ const emitWebsocketEvent = async (kafkaClient, subId, type,
11
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
12
+ data) => {
11
13
  const issuer = kafkaClient.serviceIdentity.identityName;
12
14
  // using WS_EVENT_backend since all WS_EVENTs use same schema
13
15
  const topic = `wsevent_${issuer}`;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lucaapp/service-utils",
3
- "version": "1.22.0",
3
+ "version": "1.23.0",
4
4
  "main": "dist/index.js",
5
5
  "types": "dist/index.d.ts",
6
6
  "files": [
@@ -26,20 +26,28 @@
26
26
  "@typescript-eslint/parser": "^5.30.6",
27
27
  "conventional-changelog-conventionalcommits": "^5.0.0",
28
28
  "eslint": "^8.19.0",
29
+ "eslint-plugin-prettier": "4.2.1",
29
30
  "improved-yarn-audit": "^3.0.0",
30
31
  "jest": "^28.1.2",
31
32
  "jest-junit": "^14.0.0",
33
+ "prettier": "2.7.1",
32
34
  "semantic-release": "^19.0.3",
33
35
  "semantic-release-monorepo": "^7.0.5",
36
+ "supertest": "^6.3.3",
34
37
  "ts-jest": "^28.0.5",
35
38
  "typescript": "^4.7.4"
36
39
  },
37
40
  "dependencies": {
41
+ "@asteasolutions/zod-to-openapi": "4.1.0",
38
42
  "@hapi/boom": "^10.0.0",
39
43
  "@types/express": "4.17.13",
40
44
  "@types/response-time": "^2.3.5",
45
+ "@types/supertest": "^2.0.12",
46
+ "@types/swagger-ui-express": "4.1.3",
41
47
  "axios": "^0.27.2",
42
48
  "cls-rtracer": "^2.6.2",
49
+ "express": "4.18.1",
50
+ "express-async-errors": "3.1.1",
43
51
  "jose": "4.9.2",
44
52
  "kafkajs": "2.1.0",
45
53
  "lodash": "^4.17.21",
@@ -47,7 +55,9 @@
47
55
  "pino-http": "8.2.1",
48
56
  "prom-client": "14.1.0",
49
57
  "response-time": "^2.3.2",
58
+ "swagger-ui-express": "4.5.0",
50
59
  "url-value-parser": "^2.2.0",
51
- "uuid": "^9.0.0"
60
+ "uuid": "^9.0.0",
61
+ "zod": "3.11.6"
52
62
  }
53
63
  }