transit-kit 0.2.1 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +4 -1
- package/dist/cli/generateOpenApi.d.ts +9 -0
- package/dist/cli/generateOpenApi.js +3 -0
- package/dist/cli/generateOpenApi.spec.d.ts +1 -0
- package/dist/cli/generateOpenApi.spec.js +65 -0
- package/dist/server/handlers/api/ApiEndpoint.d.ts +9 -0
- package/dist/server/handlers/api/ApiEndpoint.js +1 -0
- package/dist/server/handlers/api/EndpointDefinition.d.ts +1 -1
- package/dist/server/handlers/api/HandlerFromDefinition.d.ts +4 -4
- package/dist/server/handlers/api/createApiHandler.d.ts +11 -3
- package/dist/server/handlers/api/createApiHandler.spec.js +3 -1
- package/dist/server/server.d.ts +6 -9
- package/dist/server/server.js +14 -13
- package/package.json +3 -3
package/README.md
CHANGED
|
@@ -1 +1,4 @@
|
|
|
1
|
-
#
|
|
1
|
+
# transit-kit
|
|
2
|
+
|
|
3
|
+
[](https://github.com/D4rkr34lm/transit-kit/actions/workflows/ci.yml)
|
|
4
|
+
[](https://coveralls.io/github/D4rkr34lm/transit-kit?branch=main)
|
|
@@ -1,2 +1,11 @@
|
|
|
1
|
+
import { HttpMethod } from "../server/constants/HttpMethods";
|
|
2
|
+
import { ApiEndpointDefinition } from "../server/handlers/api/EndpointDefinition";
|
|
3
|
+
import { GenericResponseSchemaMap } from "../server/handlers/api/responses";
|
|
4
|
+
import { ZodType } from "zod";
|
|
1
5
|
import { OpenAPIV3 } from "openapi-types";
|
|
6
|
+
declare function translateToOpenAPIPathItem(definition: ApiEndpointDefinition<string, HttpMethod, ZodType | undefined, ZodType | undefined, GenericResponseSchemaMap>): [string, OpenAPIV3.PathItemObject];
|
|
2
7
|
export declare function generateOpenApiDoc(targetPath: string): Promise<OpenAPIV3.Document<{}>>;
|
|
8
|
+
export declare const __TEST_EXPORTS: {
|
|
9
|
+
translateToOpenAPIPathItem: typeof translateToOpenAPIPathItem;
|
|
10
|
+
};
|
|
11
|
+
export {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import { describe, expect, it } from "vitest";
|
|
2
|
+
import z from "zod";
|
|
3
|
+
import { createApiEndpointHandler } from "../server";
|
|
4
|
+
import { __TEST_EXPORTS } from "./generateOpenApi";
|
|
5
|
+
const { translateToOpenAPIPathItem } = __TEST_EXPORTS;
|
|
6
|
+
describe("translateToOpenAPIPathItem", () => {
|
|
7
|
+
it("should translate endpoint definitions to OpenAPI path items", () => {
|
|
8
|
+
const { definition } = createApiEndpointHandler({
|
|
9
|
+
method: "get",
|
|
10
|
+
path: "/users/:id",
|
|
11
|
+
meta: {
|
|
12
|
+
name: "getUser",
|
|
13
|
+
description: "Retrieve a user by ID",
|
|
14
|
+
group: "User",
|
|
15
|
+
},
|
|
16
|
+
responseSchemas: {
|
|
17
|
+
200: {
|
|
18
|
+
dataType: "application/json",
|
|
19
|
+
dataSchema: z.string(),
|
|
20
|
+
},
|
|
21
|
+
},
|
|
22
|
+
}, async (req) => {
|
|
23
|
+
return {
|
|
24
|
+
code: 200,
|
|
25
|
+
dataType: "application/json",
|
|
26
|
+
json: req.params.id,
|
|
27
|
+
};
|
|
28
|
+
});
|
|
29
|
+
const [path, pathItem] = translateToOpenAPIPathItem(definition);
|
|
30
|
+
const expectedPath = "/users/{id}";
|
|
31
|
+
const expectedSchema = {
|
|
32
|
+
$schema: "https://json-schema.org/draft/2020-12/schema",
|
|
33
|
+
type: "string",
|
|
34
|
+
};
|
|
35
|
+
const expectedPathItem = {
|
|
36
|
+
get: {
|
|
37
|
+
operationId: "getUser",
|
|
38
|
+
summary: "Retrieve a user by ID",
|
|
39
|
+
tags: ["User"],
|
|
40
|
+
description: "Retrieve a user by ID",
|
|
41
|
+
parameters: [
|
|
42
|
+
{
|
|
43
|
+
description: "Path parameter :id",
|
|
44
|
+
name: "id",
|
|
45
|
+
in: "path",
|
|
46
|
+
required: true,
|
|
47
|
+
schema: { type: "string" },
|
|
48
|
+
},
|
|
49
|
+
],
|
|
50
|
+
responses: {
|
|
51
|
+
"200": {
|
|
52
|
+
description: "Response for status code 200",
|
|
53
|
+
content: {
|
|
54
|
+
"application/json": {
|
|
55
|
+
schema: expectedSchema,
|
|
56
|
+
},
|
|
57
|
+
},
|
|
58
|
+
},
|
|
59
|
+
},
|
|
60
|
+
},
|
|
61
|
+
};
|
|
62
|
+
expect(path).toBe(expectedPath);
|
|
63
|
+
expect(pathItem).toEqual(expectedPathItem);
|
|
64
|
+
});
|
|
65
|
+
});
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import z from "zod";
|
|
2
|
+
import { HttpMethod } from "../../constants/HttpMethods";
|
|
3
|
+
import { ApiEndpointDefinition } from "./EndpointDefinition";
|
|
4
|
+
import { HandlerForDefinition } from "./HandlerFromDefinition";
|
|
5
|
+
import { GenericResponseSchemaMap } from "./responses";
|
|
6
|
+
export interface ApiEndpoint<Path extends string = string, Method extends HttpMethod = HttpMethod, RequestBody extends z.ZodType | undefined = undefined | z.ZodType, Query extends z.ZodType | undefined = undefined | z.ZodType, ResponseMap extends GenericResponseSchemaMap = GenericResponseSchemaMap, Handler extends HandlerForDefinition<Path, RequestBody, Query, ResponseMap> = HandlerForDefinition<Path, RequestBody, Query, ResponseMap>> {
|
|
7
|
+
definition: ApiEndpointDefinition<Path, Method, RequestBody, Query, ResponseMap>;
|
|
8
|
+
handler: Handler;
|
|
9
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -6,7 +6,7 @@ export interface ApiEndpointMeta {
|
|
|
6
6
|
group: string;
|
|
7
7
|
description: string;
|
|
8
8
|
}
|
|
9
|
-
export type ApiEndpointDefinition<Path extends string, Method extends HttpMethod, RequestBody extends z.ZodType | undefined, Query extends z.ZodType | undefined, ResponseMap extends GenericResponseSchemaMap> = {
|
|
9
|
+
export type ApiEndpointDefinition<Path extends string = string, Method extends HttpMethod = HttpMethod, RequestBody extends z.ZodType | undefined = z.ZodType | undefined, Query extends z.ZodType | undefined = z.ZodType | undefined, ResponseMap extends GenericResponseSchemaMap = GenericResponseSchemaMap> = {
|
|
10
10
|
meta: ApiEndpointMeta;
|
|
11
11
|
path: Path;
|
|
12
12
|
method: Method;
|
|
@@ -4,8 +4,8 @@ import { Prettify } from "../../utils/types";
|
|
|
4
4
|
import { ApiEndpointHandler } from "./EndpointHandler";
|
|
5
5
|
import { ExtractPathParams } from "./PathParameters";
|
|
6
6
|
import { EmptyResponse, EmptyResponseSchema } from "./responses/emptyResponse";
|
|
7
|
-
import { GenericResponseSchemaMap } from "./responses/index";
|
|
7
|
+
import { GenericResponse, GenericResponseSchemaMap } from "./responses/index";
|
|
8
8
|
import { JsonResponseSchema, JsonResponseSchemaToResponseType } from "./responses/jsonResponse";
|
|
9
|
-
export type HandlerForDefinition<Path extends string, RequestBody extends z.ZodType | undefined, Query extends z.ZodType | undefined, ResponsesMap extends GenericResponseSchemaMap> = ApiEndpointHandler<ExtractPathParams<Path>, RequestBody extends undefined ? undefined : z.infer<RequestBody>, Query extends undefined ? undefined : z.infer<Query>, Prettify<{
|
|
10
|
-
[K in keyof ResponsesMap]: K extends HttpStatusCode ? ResponsesMap[K] extends JsonResponseSchema ? JsonResponseSchemaToResponseType<K, ResponsesMap[K]> : ResponsesMap[K] extends EmptyResponseSchema ? EmptyResponse<K> : never : never;
|
|
11
|
-
}[keyof ResponsesMap]>>;
|
|
9
|
+
export type HandlerForDefinition<Path extends string, RequestBody extends z.ZodType | undefined, Query extends z.ZodType | undefined, ResponsesMap extends GenericResponseSchemaMap> = ApiEndpointHandler<ExtractPathParams<Path>, RequestBody extends undefined ? undefined : z.infer<RequestBody>, Query extends undefined ? undefined : z.infer<Query>, Exclude<Prettify<{
|
|
10
|
+
[K in keyof ResponsesMap]: K extends HttpStatusCode ? ResponsesMap[K] extends JsonResponseSchema ? JsonResponseSchemaToResponseType<K, ResponsesMap[K]> : ResponsesMap[K] extends EmptyResponseSchema ? EmptyResponse<K> : ResponsesMap[K] extends undefined ? never : GenericResponse : never;
|
|
11
|
+
}[keyof ResponsesMap]>, undefined>>;
|
|
@@ -1,11 +1,19 @@
|
|
|
1
1
|
import z from "zod";
|
|
2
2
|
import { HttpMethod } from "../../constants/HttpMethods";
|
|
3
|
+
import { Prettify } from "../../utils/types";
|
|
3
4
|
import { ApiEndpointDefinition } from "./EndpointDefinition";
|
|
4
5
|
import { ApiEndpointHandler } from "./EndpointHandler";
|
|
5
6
|
import { HandlerForDefinition } from "./HandlerFromDefinition";
|
|
6
7
|
import { GenericResponse, GenericResponseSchemaMap } from "./responses";
|
|
7
|
-
export declare function createApiEndpointHandler<const ResponsesMap extends GenericResponseSchemaMap, const Path extends string, const Method extends HttpMethod, const RequestBody extends z.ZodType | undefined = undefined, const Query extends z.ZodType | undefined = undefined>(definition: ApiEndpointDefinition<Path, Method, RequestBody, Query, ResponsesMap
|
|
8
|
-
definition:
|
|
8
|
+
export declare function createApiEndpointHandler<const ResponsesMap extends GenericResponseSchemaMap, const Path extends string, const Method extends HttpMethod, const RequestBody extends z.ZodType | undefined = undefined, const Query extends z.ZodType | undefined = undefined>(definition: Prettify<ApiEndpointDefinition<Path, Method, RequestBody, Query, ResponsesMap>>, handler: HandlerForDefinition<Path, RequestBody, Query, ResponsesMap>): {
|
|
9
|
+
definition: {
|
|
10
|
+
meta: import("./EndpointDefinition").ApiEndpointMeta;
|
|
11
|
+
path: Path;
|
|
12
|
+
method: Method;
|
|
13
|
+
requestBodySchema?: RequestBody | undefined;
|
|
14
|
+
querySchema?: Query | undefined;
|
|
15
|
+
responseSchemas: ResponsesMap;
|
|
16
|
+
};
|
|
9
17
|
handler: HandlerForDefinition<Path, RequestBody, Query, ResponsesMap>;
|
|
10
18
|
};
|
|
11
|
-
export declare function buildApiEndpointHandler
|
|
19
|
+
export declare function buildApiEndpointHandler<Handler extends ApiEndpointHandler<Record<string, string>, unknown, unknown, GenericResponse>>(handler: Handler): import("express").RequestHandler<import("express-serve-static-core").ParamsDictionary, any, any, import("qs").ParsedQs, Record<string, any>>;
|
|
@@ -2,6 +2,7 @@ import { describe, it } from "vitest";
|
|
|
2
2
|
import { createServer } from "../../server";
|
|
3
3
|
import { createApiEndpointHandler } from "./createApiHandler";
|
|
4
4
|
import testRequest from "supertest";
|
|
5
|
+
import z from "zod";
|
|
5
6
|
describe("createApiHandler", () => {
|
|
6
7
|
it("can create an API handler", () => {
|
|
7
8
|
const endpoint = createApiEndpointHandler({
|
|
@@ -12,6 +13,7 @@ describe("createApiHandler", () => {
|
|
|
12
13
|
},
|
|
13
14
|
method: "get",
|
|
14
15
|
path: "/test",
|
|
16
|
+
requestBodySchema: z.string(),
|
|
15
17
|
responseSchemas: {
|
|
16
18
|
200: {},
|
|
17
19
|
},
|
|
@@ -24,8 +26,8 @@ describe("createApiHandler", () => {
|
|
|
24
26
|
inDevMode: true,
|
|
25
27
|
port: 3000,
|
|
26
28
|
logger: false,
|
|
27
|
-
endpoints: [endpoint],
|
|
28
29
|
});
|
|
30
|
+
server.registerApiEndpoint(endpoint);
|
|
29
31
|
testRequest(server.expressApp).get("/test").expect(200);
|
|
30
32
|
});
|
|
31
33
|
});
|
package/dist/server/server.d.ts
CHANGED
|
@@ -1,23 +1,20 @@
|
|
|
1
1
|
import { Application } from "express";
|
|
2
|
-
import z from "zod";
|
|
3
|
-
import { HttpMethod } from "./constants/HttpMethods";
|
|
4
2
|
import { ApiEndpointDefinition } from "./handlers/api/EndpointDefinition";
|
|
5
|
-
import {
|
|
6
|
-
import { GenericResponse, GenericResponseSchemaMap } from "./handlers/api/responses";
|
|
3
|
+
import { HandlerForDefinition } from "./handlers/api/HandlerFromDefinition";
|
|
7
4
|
import { Logger } from "./utils/logging";
|
|
8
5
|
export interface ServerConfig {
|
|
9
6
|
inDevMode: boolean;
|
|
10
7
|
port: number;
|
|
11
8
|
logger: Logger | boolean;
|
|
12
|
-
endpoints: Array<{
|
|
13
|
-
handler: ApiEndpointHandler<Record<string, string>, any, any, GenericResponse>;
|
|
14
|
-
definition: ApiEndpointDefinition<string, HttpMethod, z.ZodType | undefined, z.ZodType | undefined, GenericResponseSchemaMap>;
|
|
15
|
-
}>;
|
|
16
9
|
}
|
|
17
10
|
export interface Server {
|
|
18
11
|
expressApp: Application;
|
|
19
12
|
logger: Logger | boolean;
|
|
20
|
-
endpointDefinitions: ApiEndpointDefinition
|
|
13
|
+
endpointDefinitions: ApiEndpointDefinition[];
|
|
14
|
+
registerApiEndpoint<Definition extends ApiEndpointDefinition>({ definition, handler, }: {
|
|
15
|
+
definition: Definition;
|
|
16
|
+
handler: HandlerForDefinition<Definition["path"], Definition["requestBodySchema"], Definition["querySchema"], Definition["responseSchemas"]>;
|
|
17
|
+
}): void;
|
|
21
18
|
start: () => void;
|
|
22
19
|
}
|
|
23
20
|
export declare function createServer(config: ServerConfig): Server;
|
package/dist/server/server.js
CHANGED
|
@@ -5,20 +5,21 @@ import { buildRequestLogger, buildResponseLogger } from "./middleware/logging";
|
|
|
5
5
|
import { buildBodyValidatorMiddleware, buildQueryValidatorMiddleware, } from "./middleware/validation";
|
|
6
6
|
import { NoOpLogger } from "./utils/logging";
|
|
7
7
|
import { hasNoValue, hasValue } from "./utils/typeGuards";
|
|
8
|
-
function registerApiEndpoint(expressApp,
|
|
8
|
+
function registerApiEndpoint(expressApp, endpoint) {
|
|
9
|
+
const { definition, handler } = endpoint;
|
|
9
10
|
const handlerStack = [
|
|
10
|
-
hasValue(
|
|
11
|
-
? buildQueryValidatorMiddleware(
|
|
11
|
+
hasValue(definition.querySchema)
|
|
12
|
+
? buildQueryValidatorMiddleware(definition.querySchema)
|
|
12
13
|
: null,
|
|
13
|
-
hasValue(
|
|
14
|
-
? buildBodyValidatorMiddleware(
|
|
14
|
+
hasValue(definition.requestBodySchema)
|
|
15
|
+
? buildBodyValidatorMiddleware(definition.requestBodySchema)
|
|
15
16
|
: null,
|
|
16
|
-
buildApiEndpointHandler(
|
|
17
|
+
buildApiEndpointHandler(handler),
|
|
17
18
|
].filter(hasValue);
|
|
18
|
-
expressApp[
|
|
19
|
+
expressApp[definition.method](definition.path, handlerStack);
|
|
19
20
|
}
|
|
20
21
|
export function createServer(config) {
|
|
21
|
-
const { port, inDevMode
|
|
22
|
+
const { port, inDevMode } = config;
|
|
22
23
|
const logger = config.logger === true
|
|
23
24
|
? console
|
|
24
25
|
: config.logger === false || hasNoValue(config.logger)
|
|
@@ -30,14 +31,14 @@ export function createServer(config) {
|
|
|
30
31
|
app.use(cookieParser());
|
|
31
32
|
app.use(buildRequestLogger(logger, inDevMode));
|
|
32
33
|
app.use(buildResponseLogger(logger, inDevMode));
|
|
33
|
-
endpoints.forEach(({ definition, handler }) => {
|
|
34
|
-
registerApiEndpoint(app, definition, handler);
|
|
35
|
-
});
|
|
36
34
|
return {
|
|
37
35
|
expressApp: app,
|
|
38
36
|
logger: logger,
|
|
39
|
-
endpointDefinitions:
|
|
40
|
-
|
|
37
|
+
endpointDefinitions: [],
|
|
38
|
+
registerApiEndpoint(endpoint) {
|
|
39
|
+
registerApiEndpoint(app, endpoint);
|
|
40
|
+
},
|
|
41
|
+
start() {
|
|
41
42
|
app.listen(port);
|
|
42
43
|
},
|
|
43
44
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "transit-kit",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.0",
|
|
4
4
|
"description": "A declarative TypeScript framework for building type-safe Express.js APIs with automatic OpenAPI generation",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"express",
|
|
@@ -28,9 +28,9 @@
|
|
|
28
28
|
],
|
|
29
29
|
"exports": {
|
|
30
30
|
"./server": {
|
|
31
|
+
"types": "./dist/server/server.d.ts",
|
|
31
32
|
"import": "./dist/server/index.js",
|
|
32
|
-
"require": "./dist/server/index.js"
|
|
33
|
-
"types": "./dist/server/server.d.ts"
|
|
33
|
+
"require": "./dist/server/index.js"
|
|
34
34
|
}
|
|
35
35
|
},
|
|
36
36
|
"bin": "./dist/cli/cli.js",
|