transit-kit 0.1.0 → 0.2.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/cli/cli.js +3 -2
- package/dist/cli/generateOpenApi.d.ts +1 -1
- package/dist/cli/generateOpenApi.js +35 -57
- package/dist/server/handlers/api/createApiHandler.d.ts +1 -1
- package/dist/server/handlers/api/createApiHandler.js +1 -1
- package/dist/server/index.d.ts +1 -1
- package/dist/server/server.d.ts +12 -11
- package/dist/server/server.js +23 -22
- package/package.json +24 -3
- package/.github/workflows/ci.yml +0 -73
- package/.github/workflows/release.yml +0 -37
- package/eslint-configs/eslint.base.config.js +0 -30
- package/eslint-configs/eslint.node.config.js +0 -23
- package/eslint-configs/eslint.test.config.js +0 -15
- package/eslint.config.ts +0 -6
- package/prettier.config.js +0 -14
- package/src/cli/cli.ts +0 -37
- package/src/cli/generateOpenApi.ts +0 -217
- package/src/server/constants/HttpMethods.ts +0 -11
- package/src/server/constants/HttpStatusCodes.ts +0 -46
- package/src/server/handlers/api/EndpointDefinition.ts +0 -24
- package/src/server/handlers/api/EndpointHandler.ts +0 -24
- package/src/server/handlers/api/HandlerFormDefinition.spec.ts +0 -120
- package/src/server/handlers/api/HandlerFromDefinition.ts +0 -33
- package/src/server/handlers/api/PathParameters.spec.ts +0 -28
- package/src/server/handlers/api/PathParameters.ts +0 -10
- package/src/server/handlers/api/createApiHandler.ts +0 -44
- package/src/server/handlers/api/responses/emptyResponse.ts +0 -12
- package/src/server/handlers/api/responses/index.ts +0 -15
- package/src/server/handlers/api/responses/jsonResponse.ts +0 -44
- package/src/server/index.ts +0 -4
- package/src/server/middleware/logging.ts +0 -41
- package/src/server/middleware/validation.ts +0 -35
- package/src/server/server.ts +0 -90
- package/src/server/utils/funcs.ts +0 -3
- package/src/server/utils/logging.ts +0 -10
- package/src/server/utils/typeGuards.ts +0 -9
- package/src/server/utils/types.ts +0 -3
- package/transitKit.code-workspace +0 -36
- package/tsconfig.json +0 -15
- package/vitest.config.ts +0 -22
|
@@ -1,217 +0,0 @@
|
|
|
1
|
-
import { glob } from "glob";
|
|
2
|
-
import path from "path";
|
|
3
|
-
import z from "zod";
|
|
4
|
-
import { HttpMethod } from "../server/constants/HttpMethods";
|
|
5
|
-
import { ApiEndpointDefinition } from "../server/handlers/api/EndpointDefinition";
|
|
6
|
-
import { GenericResponseSchemaMap } from "../server/handlers/api/responses";
|
|
7
|
-
import { hasNoValue, hasValue } from "../server/utils/typeGuards";
|
|
8
|
-
|
|
9
|
-
import { ZodType } from "zod";
|
|
10
|
-
|
|
11
|
-
import { OpenAPIV3 } from "openapi-types";
|
|
12
|
-
import { isJsonResponseSchema } from "../server/handlers/api/responses/jsonResponse";
|
|
13
|
-
|
|
14
|
-
async function findEndpointDefinitions(targetPath: string) {
|
|
15
|
-
const files = await glob("**/*.ts", {
|
|
16
|
-
cwd: path.resolve(process.cwd(), targetPath),
|
|
17
|
-
ignore: ["**/node_modules/**", "**/*.spec.ts"],
|
|
18
|
-
});
|
|
19
|
-
|
|
20
|
-
const definitions = await Promise.all(
|
|
21
|
-
files.map(async (file) => {
|
|
22
|
-
const absolutePath = path.resolve(file);
|
|
23
|
-
const fileUrl = path.toNamespacedPath(absolutePath).startsWith("/")
|
|
24
|
-
? `file://${absolutePath}`
|
|
25
|
-
: `file:///${absolutePath}`;
|
|
26
|
-
|
|
27
|
-
try {
|
|
28
|
-
const module = await import(fileUrl);
|
|
29
|
-
if (module.default) {
|
|
30
|
-
const def = module.default.__API_ENDPOINT_DEFINITION__ as
|
|
31
|
-
| ApiEndpointDefinition<
|
|
32
|
-
string,
|
|
33
|
-
HttpMethod,
|
|
34
|
-
z.ZodType | undefined,
|
|
35
|
-
z.ZodType | undefined,
|
|
36
|
-
GenericResponseSchemaMap
|
|
37
|
-
>
|
|
38
|
-
| undefined;
|
|
39
|
-
|
|
40
|
-
if (hasNoValue(def)) {
|
|
41
|
-
return null;
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
return def;
|
|
45
|
-
} else {
|
|
46
|
-
return null;
|
|
47
|
-
}
|
|
48
|
-
} catch (error) {
|
|
49
|
-
console.error(`Error importing ${file}:`, error);
|
|
50
|
-
return null;
|
|
51
|
-
}
|
|
52
|
-
}),
|
|
53
|
-
);
|
|
54
|
-
|
|
55
|
-
return definitions.filter(hasValue);
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
function extractPathAndParameters(path: string): {
|
|
59
|
-
openApiPath: string;
|
|
60
|
-
parameters: OpenAPIV3.ParameterObject[];
|
|
61
|
-
} {
|
|
62
|
-
const parameters: OpenAPIV3.ParameterObject[] =
|
|
63
|
-
path.match(/:([a-zA-Z0-9_]+)/g)?.map((param) => {
|
|
64
|
-
return {
|
|
65
|
-
name: param.substring(1),
|
|
66
|
-
in: "path",
|
|
67
|
-
required: true,
|
|
68
|
-
schema: { type: "string" },
|
|
69
|
-
description: `Path parameter ${param}`,
|
|
70
|
-
};
|
|
71
|
-
}) ?? [];
|
|
72
|
-
|
|
73
|
-
const openApiPath = path.replace(/:([a-zA-Z0-9_]+)/g, (_, paramName) => {
|
|
74
|
-
return `{${paramName}}`;
|
|
75
|
-
});
|
|
76
|
-
|
|
77
|
-
return { openApiPath, parameters };
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
function extractQueryParameters(
|
|
81
|
-
querySchema: ZodType,
|
|
82
|
-
): OpenAPIV3.ParameterObject[] {
|
|
83
|
-
const querySchemaObject = z.toJSONSchema(querySchema);
|
|
84
|
-
|
|
85
|
-
if (querySchemaObject.properties) {
|
|
86
|
-
return Object.entries(querySchemaObject.properties).map(
|
|
87
|
-
([name, schema]) => ({
|
|
88
|
-
name: name,
|
|
89
|
-
in: "query",
|
|
90
|
-
required: querySchemaObject.required?.includes(name) || false,
|
|
91
|
-
schema: schema as OpenAPIV3.SchemaObject,
|
|
92
|
-
}),
|
|
93
|
-
);
|
|
94
|
-
} else {
|
|
95
|
-
return [];
|
|
96
|
-
}
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
function translateToOpenAPIPathItem(
|
|
100
|
-
definition: ApiEndpointDefinition<
|
|
101
|
-
string,
|
|
102
|
-
HttpMethod,
|
|
103
|
-
ZodType | undefined,
|
|
104
|
-
ZodType | undefined,
|
|
105
|
-
GenericResponseSchemaMap
|
|
106
|
-
>,
|
|
107
|
-
): [string, OpenAPIV3.PathItemObject] {
|
|
108
|
-
const {
|
|
109
|
-
meta,
|
|
110
|
-
path,
|
|
111
|
-
method,
|
|
112
|
-
requestBodySchema,
|
|
113
|
-
querySchema,
|
|
114
|
-
responseSchemas,
|
|
115
|
-
} = definition;
|
|
116
|
-
|
|
117
|
-
// 1. Path and Parameter extraction
|
|
118
|
-
const { openApiPath, parameters: pathParameters } =
|
|
119
|
-
extractPathAndParameters(path);
|
|
120
|
-
|
|
121
|
-
const queryParameters = hasValue(querySchema)
|
|
122
|
-
? extractQueryParameters(querySchema)
|
|
123
|
-
: [];
|
|
124
|
-
|
|
125
|
-
const operationParameters = [...pathParameters, ...queryParameters];
|
|
126
|
-
|
|
127
|
-
const requestBody = hasValue(requestBodySchema)
|
|
128
|
-
? {
|
|
129
|
-
requestBody: {
|
|
130
|
-
description: `${meta.name} Request Body`,
|
|
131
|
-
required: true,
|
|
132
|
-
content: {
|
|
133
|
-
"application/json": {
|
|
134
|
-
schema: z.toJSONSchema(
|
|
135
|
-
requestBodySchema,
|
|
136
|
-
) as OpenAPIV3.SchemaObject, // Type assertion
|
|
137
|
-
},
|
|
138
|
-
},
|
|
139
|
-
},
|
|
140
|
-
}
|
|
141
|
-
: {};
|
|
142
|
-
|
|
143
|
-
// 4. Response Schema Translation
|
|
144
|
-
const responses = Object.entries(responseSchemas)
|
|
145
|
-
.map(([statusCode, responseDef]) => {
|
|
146
|
-
if (isJsonResponseSchema(responseDef)) {
|
|
147
|
-
const zodSchema = responseDef.dataSchema as ZodType;
|
|
148
|
-
const responseSchema = z.toJSONSchema(zodSchema);
|
|
149
|
-
|
|
150
|
-
return {
|
|
151
|
-
[statusCode]: {
|
|
152
|
-
description: `Response for status code ${statusCode}`,
|
|
153
|
-
content: {
|
|
154
|
-
[responseDef.dataType]: {
|
|
155
|
-
schema: responseSchema as OpenAPIV3.SchemaObject,
|
|
156
|
-
},
|
|
157
|
-
},
|
|
158
|
-
} as OpenAPIV3.ResponseObject,
|
|
159
|
-
};
|
|
160
|
-
} else {
|
|
161
|
-
return {
|
|
162
|
-
[statusCode]: {
|
|
163
|
-
description: `Response for status code ${statusCode}`,
|
|
164
|
-
} as OpenAPIV3.ResponseObject,
|
|
165
|
-
};
|
|
166
|
-
}
|
|
167
|
-
})
|
|
168
|
-
.reduce((acc, resp) => {
|
|
169
|
-
return { ...acc, ...resp };
|
|
170
|
-
}, {});
|
|
171
|
-
|
|
172
|
-
const operation: OpenAPIV3.OperationObject = {
|
|
173
|
-
operationId: meta.name,
|
|
174
|
-
summary: meta.description,
|
|
175
|
-
tags: [meta.group],
|
|
176
|
-
description: meta.description,
|
|
177
|
-
parameters: operationParameters,
|
|
178
|
-
...requestBody,
|
|
179
|
-
responses,
|
|
180
|
-
};
|
|
181
|
-
|
|
182
|
-
const pathItem: OpenAPIV3.PathItemObject = {
|
|
183
|
-
[method.toLowerCase()]: operation,
|
|
184
|
-
};
|
|
185
|
-
|
|
186
|
-
return [openApiPath, pathItem];
|
|
187
|
-
}
|
|
188
|
-
|
|
189
|
-
export async function generateOpenApiDoc(targetPath: string) {
|
|
190
|
-
const definitions = await findEndpointDefinitions(targetPath);
|
|
191
|
-
|
|
192
|
-
const paths = definitions.reduce<OpenAPIV3.PathsObject>((acc, def) => {
|
|
193
|
-
const [openApiPath, pathItem] = translateToOpenAPIPathItem(def);
|
|
194
|
-
|
|
195
|
-
if (acc[openApiPath]) {
|
|
196
|
-
acc[openApiPath] = {
|
|
197
|
-
...acc[openApiPath],
|
|
198
|
-
...pathItem,
|
|
199
|
-
};
|
|
200
|
-
} else {
|
|
201
|
-
acc[openApiPath] = pathItem;
|
|
202
|
-
}
|
|
203
|
-
|
|
204
|
-
return acc;
|
|
205
|
-
}, {});
|
|
206
|
-
|
|
207
|
-
const openApiDocument: OpenAPIV3.Document = {
|
|
208
|
-
openapi: "3.0.0",
|
|
209
|
-
info: {
|
|
210
|
-
title: "Generated API",
|
|
211
|
-
version: "1.0.0",
|
|
212
|
-
},
|
|
213
|
-
paths: paths,
|
|
214
|
-
};
|
|
215
|
-
|
|
216
|
-
return openApiDocument;
|
|
217
|
-
}
|
|
@@ -1,46 +0,0 @@
|
|
|
1
|
-
export const SuccessStatusCodes = {
|
|
2
|
-
Ok_200: 200,
|
|
3
|
-
Created_201: 201,
|
|
4
|
-
NoContent_204: 204,
|
|
5
|
-
} as const;
|
|
6
|
-
|
|
7
|
-
export type SuccessStatusCode =
|
|
8
|
-
(typeof SuccessStatusCodes)[keyof typeof SuccessStatusCodes];
|
|
9
|
-
|
|
10
|
-
export const ClientErrorStatusCodes = {
|
|
11
|
-
BadRequest_400: 400,
|
|
12
|
-
Unauthorized_401: 401,
|
|
13
|
-
Forbidden_403: 403,
|
|
14
|
-
NotFound_404: 404,
|
|
15
|
-
Conflict_409: 409,
|
|
16
|
-
} as const;
|
|
17
|
-
|
|
18
|
-
export type ClientErrorStatusCode =
|
|
19
|
-
(typeof ClientErrorStatusCodes)[keyof typeof ClientErrorStatusCodes];
|
|
20
|
-
|
|
21
|
-
export const ServerErrorStatusCodes = {
|
|
22
|
-
InternalServerError_500: 500,
|
|
23
|
-
NotImplemented_501: 501,
|
|
24
|
-
BadGateway_502: 502,
|
|
25
|
-
ServiceUnavailable_503: 503,
|
|
26
|
-
} as const;
|
|
27
|
-
|
|
28
|
-
export const ErrorStatusCodes = {
|
|
29
|
-
...ClientErrorStatusCodes,
|
|
30
|
-
...ServerErrorStatusCodes,
|
|
31
|
-
} as const;
|
|
32
|
-
|
|
33
|
-
export type ErrorStatusCode =
|
|
34
|
-
(typeof ErrorStatusCodes)[keyof typeof ErrorStatusCodes];
|
|
35
|
-
|
|
36
|
-
export type ServerErrorStatusCode =
|
|
37
|
-
(typeof ServerErrorStatusCodes)[keyof typeof ServerErrorStatusCodes];
|
|
38
|
-
|
|
39
|
-
export const HttpStatusCodes = {
|
|
40
|
-
...SuccessStatusCodes,
|
|
41
|
-
...ClientErrorStatusCodes,
|
|
42
|
-
...ServerErrorStatusCodes,
|
|
43
|
-
} as const;
|
|
44
|
-
|
|
45
|
-
export type HttpStatusCode =
|
|
46
|
-
(typeof HttpStatusCodes)[keyof typeof HttpStatusCodes];
|
|
@@ -1,24 +0,0 @@
|
|
|
1
|
-
import z from "zod";
|
|
2
|
-
import { HttpMethod } from "../../constants/HttpMethods";
|
|
3
|
-
import { GenericResponseSchemaMap } from "./responses/index";
|
|
4
|
-
|
|
5
|
-
export interface ApiEndpointMeta {
|
|
6
|
-
name: string;
|
|
7
|
-
group: string;
|
|
8
|
-
description: string;
|
|
9
|
-
}
|
|
10
|
-
|
|
11
|
-
export type ApiEndpointDefinition<
|
|
12
|
-
Path extends string,
|
|
13
|
-
Method extends HttpMethod,
|
|
14
|
-
RequestBody extends z.ZodType | undefined,
|
|
15
|
-
Query extends z.ZodType | undefined,
|
|
16
|
-
ResponseMap extends GenericResponseSchemaMap,
|
|
17
|
-
> = {
|
|
18
|
-
meta: ApiEndpointMeta;
|
|
19
|
-
path: Path;
|
|
20
|
-
method: Method;
|
|
21
|
-
requestBodySchema?: RequestBody;
|
|
22
|
-
querySchema?: Query;
|
|
23
|
-
responseSchemas: ResponseMap;
|
|
24
|
-
};
|
|
@@ -1,24 +0,0 @@
|
|
|
1
|
-
import { Request } from "express";
|
|
2
|
-
|
|
3
|
-
import { HttpStatusCodes } from "../../constants/HttpStatusCodes";
|
|
4
|
-
import { GenericResponse } from "./responses";
|
|
5
|
-
|
|
6
|
-
export type ApiEndpointHandler<
|
|
7
|
-
PathParams extends Record<string, string> | undefined = {},
|
|
8
|
-
RequestBody = unknown,
|
|
9
|
-
Query = unknown,
|
|
10
|
-
Responses extends GenericResponse = never,
|
|
11
|
-
> = (
|
|
12
|
-
request: Request<
|
|
13
|
-
PathParams,
|
|
14
|
-
unknown,
|
|
15
|
-
RequestBody,
|
|
16
|
-
Query,
|
|
17
|
-
Record<string, unknown>
|
|
18
|
-
>,
|
|
19
|
-
) => Promise<
|
|
20
|
-
| Responses
|
|
21
|
-
| {
|
|
22
|
-
code: (typeof HttpStatusCodes)["InternalServerError_500"];
|
|
23
|
-
}
|
|
24
|
-
>;
|
|
@@ -1,120 +0,0 @@
|
|
|
1
|
-
import { Request } from "express";
|
|
2
|
-
import { describe, expectTypeOf, it } from "vitest";
|
|
3
|
-
import z from "zod";
|
|
4
|
-
import { HandlerForDefinition } from "./HandlerFromDefinition";
|
|
5
|
-
|
|
6
|
-
describe("HandlerFromDefinition", () => {
|
|
7
|
-
it("can infer handler responses correctly (Empty)", () => {
|
|
8
|
-
type Handler = HandlerForDefinition<
|
|
9
|
-
"/test",
|
|
10
|
-
undefined,
|
|
11
|
-
undefined,
|
|
12
|
-
{
|
|
13
|
-
200: {};
|
|
14
|
-
}
|
|
15
|
-
>;
|
|
16
|
-
|
|
17
|
-
type Expected = (
|
|
18
|
-
request: Request<
|
|
19
|
-
undefined,
|
|
20
|
-
unknown,
|
|
21
|
-
undefined,
|
|
22
|
-
undefined,
|
|
23
|
-
Record<string, unknown>
|
|
24
|
-
>,
|
|
25
|
-
) => Promise<
|
|
26
|
-
| {
|
|
27
|
-
code: 200;
|
|
28
|
-
}
|
|
29
|
-
| {
|
|
30
|
-
code: 500;
|
|
31
|
-
}
|
|
32
|
-
>;
|
|
33
|
-
|
|
34
|
-
expectTypeOf<Handler>().toEqualTypeOf<Expected>();
|
|
35
|
-
});
|
|
36
|
-
|
|
37
|
-
it("can infer handler responses correctly (Json)", () => {
|
|
38
|
-
const _data = z.object({
|
|
39
|
-
a: z.number(),
|
|
40
|
-
});
|
|
41
|
-
|
|
42
|
-
type Handler = HandlerForDefinition<
|
|
43
|
-
"/test",
|
|
44
|
-
undefined,
|
|
45
|
-
undefined,
|
|
46
|
-
{
|
|
47
|
-
200: {
|
|
48
|
-
dataType: "application/json";
|
|
49
|
-
dataSchema: typeof _data;
|
|
50
|
-
};
|
|
51
|
-
}
|
|
52
|
-
>;
|
|
53
|
-
|
|
54
|
-
type Expected = (
|
|
55
|
-
request: Request<
|
|
56
|
-
undefined,
|
|
57
|
-
unknown,
|
|
58
|
-
undefined,
|
|
59
|
-
undefined,
|
|
60
|
-
Record<string, unknown>
|
|
61
|
-
>,
|
|
62
|
-
) => Promise<
|
|
63
|
-
| {
|
|
64
|
-
code: 200;
|
|
65
|
-
dataType: "application/json";
|
|
66
|
-
json: {
|
|
67
|
-
a: number;
|
|
68
|
-
};
|
|
69
|
-
}
|
|
70
|
-
| {
|
|
71
|
-
code: 500;
|
|
72
|
-
}
|
|
73
|
-
>;
|
|
74
|
-
|
|
75
|
-
expectTypeOf<Handler>().toEqualTypeOf<Expected>();
|
|
76
|
-
});
|
|
77
|
-
|
|
78
|
-
it("can infer handler responses correctly (Multiple)", () => {
|
|
79
|
-
const _data = z.object({
|
|
80
|
-
a: z.number(),
|
|
81
|
-
});
|
|
82
|
-
|
|
83
|
-
type Handler = HandlerForDefinition<
|
|
84
|
-
"/test",
|
|
85
|
-
undefined,
|
|
86
|
-
undefined,
|
|
87
|
-
{
|
|
88
|
-
200: {
|
|
89
|
-
dataType: "application/json";
|
|
90
|
-
dataSchema: typeof _data;
|
|
91
|
-
};
|
|
92
|
-
400: {};
|
|
93
|
-
}
|
|
94
|
-
>;
|
|
95
|
-
|
|
96
|
-
type Expected = (
|
|
97
|
-
request: Request<
|
|
98
|
-
undefined,
|
|
99
|
-
unknown,
|
|
100
|
-
undefined,
|
|
101
|
-
undefined,
|
|
102
|
-
Record<string, unknown>
|
|
103
|
-
>,
|
|
104
|
-
) => Promise<
|
|
105
|
-
| {
|
|
106
|
-
code: 200;
|
|
107
|
-
dataType: "application/json";
|
|
108
|
-
json: {
|
|
109
|
-
a: number;
|
|
110
|
-
};
|
|
111
|
-
}
|
|
112
|
-
| { code: 400 }
|
|
113
|
-
| {
|
|
114
|
-
code: 500;
|
|
115
|
-
}
|
|
116
|
-
>;
|
|
117
|
-
|
|
118
|
-
expectTypeOf<Handler>().toEqualTypeOf<Expected>();
|
|
119
|
-
});
|
|
120
|
-
});
|
|
@@ -1,33 +0,0 @@
|
|
|
1
|
-
import z from "zod";
|
|
2
|
-
import { HttpStatusCode } from "../../constants/HttpStatusCodes";
|
|
3
|
-
import { Prettify } from "../../utils/types";
|
|
4
|
-
import { ApiEndpointHandler } from "./EndpointHandler";
|
|
5
|
-
import { ExtractPathParams } from "./PathParameters";
|
|
6
|
-
import { EmptyResponse, EmptyResponseSchema } from "./responses/emptyResponse";
|
|
7
|
-
import { GenericResponseSchemaMap } from "./responses/index";
|
|
8
|
-
import {
|
|
9
|
-
JsonResponseSchema,
|
|
10
|
-
JsonResponseSchemaToResponseType,
|
|
11
|
-
} from "./responses/jsonResponse";
|
|
12
|
-
|
|
13
|
-
export type HandlerForDefinition<
|
|
14
|
-
Path extends string,
|
|
15
|
-
RequestBody extends z.ZodType | undefined,
|
|
16
|
-
Query extends z.ZodType | undefined,
|
|
17
|
-
ResponsesMap extends GenericResponseSchemaMap,
|
|
18
|
-
> = ApiEndpointHandler<
|
|
19
|
-
ExtractPathParams<Path>,
|
|
20
|
-
RequestBody extends undefined ? undefined : z.infer<RequestBody>,
|
|
21
|
-
Query extends undefined ? undefined : z.infer<Query>,
|
|
22
|
-
Prettify<
|
|
23
|
-
{
|
|
24
|
-
[K in keyof ResponsesMap]: K extends HttpStatusCode
|
|
25
|
-
? ResponsesMap[K] extends JsonResponseSchema
|
|
26
|
-
? JsonResponseSchemaToResponseType<K, ResponsesMap[K]>
|
|
27
|
-
: ResponsesMap[K] extends EmptyResponseSchema
|
|
28
|
-
? EmptyResponse<K>
|
|
29
|
-
: never
|
|
30
|
-
: never;
|
|
31
|
-
}[keyof ResponsesMap]
|
|
32
|
-
>
|
|
33
|
-
>;
|
|
@@ -1,28 +0,0 @@
|
|
|
1
|
-
import { describe, expectTypeOf, it } from "vitest";
|
|
2
|
-
import { ExtractPathParams } from "./PathParameters";
|
|
3
|
-
|
|
4
|
-
describe("ExtractPathParameters", () => {
|
|
5
|
-
it("can infer the parameter types of a path", () => {
|
|
6
|
-
type Path = "/test/path/with/:testId";
|
|
7
|
-
type ParamObject = ExtractPathParams<Path>;
|
|
8
|
-
|
|
9
|
-
expectTypeOf<ParamObject>().toEqualTypeOf<{ testId: string }>();
|
|
10
|
-
});
|
|
11
|
-
|
|
12
|
-
it("can infer multiple params", () => {
|
|
13
|
-
type Path = "/test/path/with/:testId/and/:otherId";
|
|
14
|
-
type ParamObject = ExtractPathParams<Path>;
|
|
15
|
-
|
|
16
|
-
expectTypeOf<ParamObject>().toEqualTypeOf<{
|
|
17
|
-
testId: string;
|
|
18
|
-
otherId: string;
|
|
19
|
-
}>();
|
|
20
|
-
});
|
|
21
|
-
|
|
22
|
-
it("can infer correctly if no param is present", () => {
|
|
23
|
-
type Path = "/test/path/without";
|
|
24
|
-
type ParamObject = ExtractPathParams<Path>;
|
|
25
|
-
|
|
26
|
-
expectTypeOf<ParamObject>().toEqualTypeOf<undefined>();
|
|
27
|
-
});
|
|
28
|
-
});
|
|
@@ -1,10 +0,0 @@
|
|
|
1
|
-
export type ExtractPathParams<Path extends string> =
|
|
2
|
-
Path extends `${string}:${infer Param}/${infer Rest}`
|
|
3
|
-
? {
|
|
4
|
-
[K in Param | keyof ExtractPathParams<Rest>]: string;
|
|
5
|
-
}
|
|
6
|
-
: Path extends `${string}:${infer Param}`
|
|
7
|
-
? {
|
|
8
|
-
[K in Param]: string;
|
|
9
|
-
}
|
|
10
|
-
: undefined;
|
|
@@ -1,44 +0,0 @@
|
|
|
1
|
-
import { Request, Response } from "express";
|
|
2
|
-
import expressAsyncHandler from "express-async-handler";
|
|
3
|
-
import z from "zod";
|
|
4
|
-
import { HttpMethod } from "../../constants/HttpMethods";
|
|
5
|
-
import { ApiEndpointDefinition } from "./EndpointDefinition";
|
|
6
|
-
import { ApiEndpointHandler } from "./EndpointHandler";
|
|
7
|
-
import { HandlerForDefinition } from "./HandlerFromDefinition";
|
|
8
|
-
import { GenericResponseSchemaMap } from "./responses";
|
|
9
|
-
import { isJsonResponse } from "./responses/jsonResponse";
|
|
10
|
-
|
|
11
|
-
export function createApiEndpointHandler<
|
|
12
|
-
const ResponsesMap extends GenericResponseSchemaMap,
|
|
13
|
-
const Path extends string,
|
|
14
|
-
const Method extends HttpMethod,
|
|
15
|
-
const RequestBody extends z.ZodType | undefined = undefined,
|
|
16
|
-
const Query extends z.ZodType | undefined = undefined,
|
|
17
|
-
>(
|
|
18
|
-
definition: ApiEndpointDefinition<
|
|
19
|
-
Path,
|
|
20
|
-
Method,
|
|
21
|
-
RequestBody,
|
|
22
|
-
Query,
|
|
23
|
-
ResponsesMap
|
|
24
|
-
>,
|
|
25
|
-
handler: HandlerForDefinition<Path, RequestBody, Query, ResponsesMap>,
|
|
26
|
-
) {
|
|
27
|
-
return {
|
|
28
|
-
__API_ENDPOINT_DEFINITION__: definition,
|
|
29
|
-
definition,
|
|
30
|
-
handler,
|
|
31
|
-
};
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
export function buildApiEndpointHandler(handler: ApiEndpointHandler) {
|
|
35
|
-
return expressAsyncHandler(async (request: Request, response: Response) => {
|
|
36
|
-
const result = await handler(request);
|
|
37
|
-
|
|
38
|
-
if (isJsonResponse(result)) {
|
|
39
|
-
response.status(result.code).json(result.json);
|
|
40
|
-
} else {
|
|
41
|
-
response.status(result.code).send();
|
|
42
|
-
}
|
|
43
|
-
});
|
|
44
|
-
}
|
|
@@ -1,12 +0,0 @@
|
|
|
1
|
-
import { HttpStatusCode } from "../../../constants/HttpStatusCodes";
|
|
2
|
-
|
|
3
|
-
export interface EmptyResponseSchema {}
|
|
4
|
-
|
|
5
|
-
export interface EmptyResponse<Code extends HttpStatusCode = HttpStatusCode> {
|
|
6
|
-
code: Code;
|
|
7
|
-
}
|
|
8
|
-
|
|
9
|
-
export type EmptyResponseSchemaToResponseType<
|
|
10
|
-
Code extends HttpStatusCode,
|
|
11
|
-
_ extends EmptyResponseSchema,
|
|
12
|
-
> = EmptyResponse<Code>;
|
|
@@ -1,15 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
ClientErrorStatusCode,
|
|
3
|
-
SuccessStatusCode,
|
|
4
|
-
} from "../../../constants/HttpStatusCodes";
|
|
5
|
-
import { EmptyResponse, EmptyResponseSchema } from "./emptyResponse";
|
|
6
|
-
import { JsonResponse, JsonResponseSchema } from "./jsonResponse";
|
|
7
|
-
|
|
8
|
-
export type GenericResponseSchemaMap = {
|
|
9
|
-
[K in SuccessStatusCode | ClientErrorStatusCode]?:
|
|
10
|
-
| EmptyResponseSchema
|
|
11
|
-
| JsonResponseSchema
|
|
12
|
-
| undefined;
|
|
13
|
-
};
|
|
14
|
-
|
|
15
|
-
export type GenericResponse = EmptyResponse | JsonResponse;
|
|
@@ -1,44 +0,0 @@
|
|
|
1
|
-
import z from "zod";
|
|
2
|
-
import { HttpStatusCode } from "../../../constants/HttpStatusCodes";
|
|
3
|
-
import { hasValue } from "../../../utils/typeGuards";
|
|
4
|
-
|
|
5
|
-
export interface JsonResponseSchema<DataSchema extends z.ZodType = z.ZodType> {
|
|
6
|
-
dataType: "application/json";
|
|
7
|
-
dataSchema: DataSchema;
|
|
8
|
-
}
|
|
9
|
-
|
|
10
|
-
export interface JsonResponse<
|
|
11
|
-
Code extends HttpStatusCode = HttpStatusCode,
|
|
12
|
-
Data = unknown,
|
|
13
|
-
> {
|
|
14
|
-
code: Code;
|
|
15
|
-
dataType: "application/json";
|
|
16
|
-
json: Data;
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
export type JsonResponseSchemaToResponseType<
|
|
20
|
-
Code extends HttpStatusCode,
|
|
21
|
-
Schema extends JsonResponseSchema,
|
|
22
|
-
> =
|
|
23
|
-
Schema extends JsonResponseSchema<infer DataSchema>
|
|
24
|
-
? JsonResponse<Code, z.infer<DataSchema>>
|
|
25
|
-
: never;
|
|
26
|
-
|
|
27
|
-
export function isJsonResponseSchema(
|
|
28
|
-
value: unknown,
|
|
29
|
-
): value is JsonResponseSchema {
|
|
30
|
-
return (
|
|
31
|
-
typeof value === "object" &&
|
|
32
|
-
hasValue(value) &&
|
|
33
|
-
"dataType" in value &&
|
|
34
|
-
value.dataType === "application/json"
|
|
35
|
-
);
|
|
36
|
-
}
|
|
37
|
-
export function isJsonResponse(value: unknown): value is JsonResponse {
|
|
38
|
-
return (
|
|
39
|
-
typeof value === "object" &&
|
|
40
|
-
value !== null &&
|
|
41
|
-
"dataType" in value &&
|
|
42
|
-
value.dataType === "application/json"
|
|
43
|
-
);
|
|
44
|
-
}
|
package/src/server/index.ts
DELETED
|
@@ -1,41 +0,0 @@
|
|
|
1
|
-
import colors from "colors/safe";
|
|
2
|
-
import { NextFunction, Request, Response } from "express";
|
|
3
|
-
import { Logger } from "../utils/logging";
|
|
4
|
-
|
|
5
|
-
export function buildRequestLogger(logger: Logger, isInDevMode: boolean) {
|
|
6
|
-
return (req: Request, res: Response, next: NextFunction) => {
|
|
7
|
-
logger.info(
|
|
8
|
-
`[Request] ${colors.cyan(req.method)} - ${colors.cyan(req.path)}`,
|
|
9
|
-
);
|
|
10
|
-
|
|
11
|
-
if (isInDevMode) {
|
|
12
|
-
logger.info(`[Request - Dev] Headers: ${JSON.stringify(req.headers)}`);
|
|
13
|
-
logger.info(`[Request - Dev] Query: ${JSON.stringify(req.query)}`);
|
|
14
|
-
logger.info(`[Request - Dev] Body: ${JSON.stringify(req.body)}`);
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
next();
|
|
18
|
-
};
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
export function buildResponseLogger(logger: Logger, isInDevMode: boolean) {
|
|
22
|
-
return (req: Request, res: Response, next: NextFunction) => {
|
|
23
|
-
const originalSend = res.send;
|
|
24
|
-
|
|
25
|
-
res.json = function (body: unknown): Response {
|
|
26
|
-
logger.info(
|
|
27
|
-
`[Response] ${colors.cyan(req.method)} - ${colors.cyan(
|
|
28
|
-
req.path,
|
|
29
|
-
)} - Status: ${res.statusCode > 299 && res.statusCode < 599 ? colors.red(res.statusCode.toString()) : colors.green(res.statusCode.toString())}`,
|
|
30
|
-
);
|
|
31
|
-
|
|
32
|
-
if (isInDevMode) {
|
|
33
|
-
logger.info(`[Response - Dev] Body: ${JSON.stringify(body)}`);
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
return originalSend.call(this, body);
|
|
37
|
-
};
|
|
38
|
-
|
|
39
|
-
next();
|
|
40
|
-
};
|
|
41
|
-
}
|