transit-kit 0.1.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.
Files changed (78) hide show
  1. package/.github/workflows/ci.yml +73 -0
  2. package/.github/workflows/release.yml +37 -0
  3. package/README.md +1 -0
  4. package/dist/cli/cli.d.ts +1 -0
  5. package/dist/cli/cli.js +20 -0
  6. package/dist/cli/generateOpenApi.d.ts +2 -0
  7. package/dist/cli/generateOpenApi.js +152 -0
  8. package/dist/server/constants/HttpMethods.d.ts +10 -0
  9. package/dist/server/constants/HttpMethods.js +9 -0
  10. package/dist/server/constants/HttpStatusCodes.d.ts +48 -0
  11. package/dist/server/constants/HttpStatusCodes.js +27 -0
  12. package/dist/server/handlers/api/EndpointDefinition.d.ts +16 -0
  13. package/dist/server/handlers/api/EndpointDefinition.js +1 -0
  14. package/dist/server/handlers/api/EndpointHandler.d.ts +6 -0
  15. package/dist/server/handlers/api/EndpointHandler.js +1 -0
  16. package/dist/server/handlers/api/HandlerFormDefinition.spec.d.ts +1 -0
  17. package/dist/server/handlers/api/HandlerFormDefinition.spec.js +19 -0
  18. package/dist/server/handlers/api/HandlerFromDefinition.d.ts +11 -0
  19. package/dist/server/handlers/api/HandlerFromDefinition.js +1 -0
  20. package/dist/server/handlers/api/PathParameters.d.ts +5 -0
  21. package/dist/server/handlers/api/PathParameters.js +1 -0
  22. package/dist/server/handlers/api/PathParameters.spec.d.ts +1 -0
  23. package/dist/server/handlers/api/PathParameters.spec.js +12 -0
  24. package/dist/server/handlers/api/createApiHandler.d.ts +12 -0
  25. package/dist/server/handlers/api/createApiHandler.js +20 -0
  26. package/dist/server/handlers/api/responses/emptyResponse.d.ts +7 -0
  27. package/dist/server/handlers/api/responses/emptyResponse.js +1 -0
  28. package/dist/server/handlers/api/responses/index.d.ts +7 -0
  29. package/dist/server/handlers/api/responses/index.js +1 -0
  30. package/dist/server/handlers/api/responses/jsonResponse.d.ts +14 -0
  31. package/dist/server/handlers/api/responses/jsonResponse.js +13 -0
  32. package/dist/server/index.d.ts +4 -0
  33. package/dist/server/index.js +3 -0
  34. package/dist/server/middleware/logging.d.ts +4 -0
  35. package/dist/server/middleware/logging.js +25 -0
  36. package/dist/server/middleware/validation.d.ts +4 -0
  37. package/dist/server/middleware/validation.js +27 -0
  38. package/dist/server/server.d.ts +21 -0
  39. package/dist/server/server.js +43 -0
  40. package/dist/server/utils/funcs.d.ts +1 -0
  41. package/dist/server/utils/funcs.js +3 -0
  42. package/dist/server/utils/logging.d.ts +3 -0
  43. package/dist/server/utils/logging.js +7 -0
  44. package/dist/server/utils/typeGuards.d.ts +2 -0
  45. package/dist/server/utils/typeGuards.js +6 -0
  46. package/dist/server/utils/types.d.ts +3 -0
  47. package/dist/server/utils/types.js +1 -0
  48. package/eslint-configs/eslint.base.config.js +30 -0
  49. package/eslint-configs/eslint.node.config.js +23 -0
  50. package/eslint-configs/eslint.test.config.js +15 -0
  51. package/eslint.config.ts +6 -0
  52. package/package.json +46 -0
  53. package/prettier.config.js +14 -0
  54. package/src/cli/cli.ts +37 -0
  55. package/src/cli/generateOpenApi.ts +217 -0
  56. package/src/server/constants/HttpMethods.ts +11 -0
  57. package/src/server/constants/HttpStatusCodes.ts +46 -0
  58. package/src/server/handlers/api/EndpointDefinition.ts +24 -0
  59. package/src/server/handlers/api/EndpointHandler.ts +24 -0
  60. package/src/server/handlers/api/HandlerFormDefinition.spec.ts +120 -0
  61. package/src/server/handlers/api/HandlerFromDefinition.ts +33 -0
  62. package/src/server/handlers/api/PathParameters.spec.ts +28 -0
  63. package/src/server/handlers/api/PathParameters.ts +10 -0
  64. package/src/server/handlers/api/createApiHandler.ts +44 -0
  65. package/src/server/handlers/api/responses/emptyResponse.ts +12 -0
  66. package/src/server/handlers/api/responses/index.ts +15 -0
  67. package/src/server/handlers/api/responses/jsonResponse.ts +44 -0
  68. package/src/server/index.ts +4 -0
  69. package/src/server/middleware/logging.ts +41 -0
  70. package/src/server/middleware/validation.ts +35 -0
  71. package/src/server/server.ts +90 -0
  72. package/src/server/utils/funcs.ts +3 -0
  73. package/src/server/utils/logging.ts +10 -0
  74. package/src/server/utils/typeGuards.ts +9 -0
  75. package/src/server/utils/types.ts +3 -0
  76. package/transitKit.code-workspace +36 -0
  77. package/tsconfig.json +15 -0
  78. package/vitest.config.ts +22 -0
@@ -0,0 +1,21 @@
1
+ import { Application } from "express";
2
+ import z from "zod";
3
+ import { HttpMethod } from "./constants/HttpMethods";
4
+ import { ApiEndpointDefinition } from "./handlers/api/EndpointDefinition";
5
+ import { ApiEndpointHandler } from "./handlers/api/EndpointHandler";
6
+ import { Logger } from "./utils/logging";
7
+ export interface ServerOptions {
8
+ inDevMode?: boolean;
9
+ port?: number;
10
+ logger?: Logger | boolean;
11
+ }
12
+ export interface Server {
13
+ _expressApp: Application;
14
+ _logger: Logger | boolean;
15
+ start: () => void;
16
+ registerApiEndpoint: (endpoint: {
17
+ endpointHandler: ApiEndpointHandler;
18
+ endpointDefinition: ApiEndpointDefinition<string, HttpMethod, z.ZodType, z.ZodType, {}>;
19
+ }) => void;
20
+ }
21
+ export declare function createServer(options: ServerOptions): Server;
@@ -0,0 +1,43 @@
1
+ import cookieParser from "cookie-parser";
2
+ import express from "express";
3
+ import { buildApiEndpointHandler } from "./handlers/api/createApiHandler";
4
+ import { buildRequestLogger, buildResponseLogger } from "./middleware/logging";
5
+ import { buildBodyValidatorMiddleware, buildQueryValidatorMiddleware, } from "./middleware/validation";
6
+ import { NoOpLogger } from "./utils/logging";
7
+ import { hasNoValue, hasValue } from "./utils/typeGuards";
8
+ export function createServer(options) {
9
+ const { port = 3000, inDevMode = false } = options;
10
+ const logger = options.logger === true
11
+ ? console
12
+ : options.logger === false || hasNoValue(options.logger)
13
+ ? NoOpLogger
14
+ : options.logger;
15
+ const app = express();
16
+ app.use(express.json());
17
+ app.use(express.urlencoded({ extended: true }));
18
+ app.use(cookieParser());
19
+ app.use(buildRequestLogger(logger, inDevMode));
20
+ app.use(buildResponseLogger(logger, inDevMode));
21
+ return {
22
+ _expressApp: app,
23
+ _logger: logger,
24
+ start: () => {
25
+ app.listen(port);
26
+ },
27
+ registerApiEndpoint: ({ endpointDefinition, endpointHandler }) => {
28
+ registerApiEndpoint(app, endpointDefinition, endpointHandler);
29
+ },
30
+ };
31
+ }
32
+ function registerApiEndpoint(expressApp, endpointDefinition, endpointHandler) {
33
+ const handlerStack = [
34
+ hasValue(endpointDefinition.querySchema)
35
+ ? buildQueryValidatorMiddleware(endpointDefinition.querySchema)
36
+ : null,
37
+ hasValue(endpointDefinition.requestBodySchema)
38
+ ? buildBodyValidatorMiddleware(endpointDefinition.requestBodySchema)
39
+ : null,
40
+ buildApiEndpointHandler(endpointHandler),
41
+ ].filter(hasValue);
42
+ expressApp[endpointDefinition.method](endpointDefinition.path, handlerStack);
43
+ }
@@ -0,0 +1 @@
1
+ export declare function isEmpty<T>(array: Array<T>): boolean;
@@ -0,0 +1,3 @@
1
+ export function isEmpty(array) {
2
+ return array.length === 0;
3
+ }
@@ -0,0 +1,3 @@
1
+ export type Logger = Pick<Console, "log" | "info" | "error" | "warn">;
2
+ export declare const NoOpLogger: Logger;
3
+ export declare const DefaultLogger: Logger;
@@ -0,0 +1,7 @@
1
+ export const NoOpLogger = {
2
+ log: () => { },
3
+ info: () => { },
4
+ error: () => { },
5
+ warn: () => { },
6
+ };
7
+ export const DefaultLogger = console;
@@ -0,0 +1,2 @@
1
+ export declare function hasValue<T>(value: T | undefined | null): value is T;
2
+ export declare function hasNoValue<T>(value: T | undefined | null): value is undefined | null;
@@ -0,0 +1,6 @@
1
+ export function hasValue(value) {
2
+ return value !== undefined && value !== null;
3
+ }
4
+ export function hasNoValue(value) {
5
+ return value === undefined || value === null;
6
+ }
@@ -0,0 +1,3 @@
1
+ export type Prettify<T> = {
2
+ [K in keyof T]: T[K];
3
+ } & {};
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,30 @@
1
+ import { globalIgnores } from "eslint/config";
2
+ import { config } from "typescript-eslint";
3
+
4
+ import eslintJS from "@eslint/js";
5
+ import prettierEslint from "eslint-plugin-prettier/recommended";
6
+ import eslintTS from "typescript-eslint";
7
+
8
+ export default config(
9
+ globalIgnores([
10
+ "*.config.{mjs|js|ts}",
11
+ "coverage",
12
+ "dist",
13
+ "generated",
14
+ "node_modules",
15
+ ]),
16
+ eslintJS.configs.recommended,
17
+ eslintTS.configs.recommended,
18
+ {
19
+ rules: {
20
+ "@typescript-eslint/no-unused-vars": [
21
+ "error",
22
+ {
23
+ argsIgnorePattern: "^_",
24
+ destructuredArrayIgnorePattern: "^_",
25
+ },
26
+ ],
27
+ },
28
+ },
29
+ prettierEslint,
30
+ );
@@ -0,0 +1,23 @@
1
+ import globals from "globals";
2
+ import { config } from "typescript-eslint";
3
+
4
+ import baseConfig from "./eslint.base.config.js";
5
+
6
+ export default config(baseConfig, {
7
+ files: ["**/*.ts"],
8
+ languageOptions: {
9
+ globals: {
10
+ ...globals.node,
11
+ },
12
+ },
13
+ rules: {
14
+ "@typescript-eslint/no-empty-object-type": "off",
15
+ "@typescript-eslint/no-unused-vars": [
16
+ "error",
17
+ {
18
+ argsIgnorePattern: "^_",
19
+ varsIgnorePattern: "^_",
20
+ },
21
+ ],
22
+ },
23
+ });
@@ -0,0 +1,15 @@
1
+ import eslintVitest from "@vitest/eslint-plugin";
2
+ import globals from "globals";
3
+ import { config } from "typescript-eslint";
4
+
5
+ export default config(eslintVitest.configs.recommended, {
6
+ files: ["**/*.spec.ts"],
7
+ languageOptions: {
8
+ globals: {
9
+ ...globals.vitest,
10
+ },
11
+ },
12
+ rules: {
13
+ "vitest/expect-expect": "off",
14
+ },
15
+ });
@@ -0,0 +1,6 @@
1
+ import { config } from "typescript-eslint";
2
+
3
+ import nodeConfig from "./eslint-configs/eslint.node.config";
4
+ import testConfig from "./eslint-configs/eslint.test.config";
5
+
6
+ export default config(nodeConfig, testConfig);
package/package.json ADDED
@@ -0,0 +1,46 @@
1
+ {
2
+ "name": "transit-kit",
3
+ "version": "0.1.0",
4
+ "type": "module",
5
+ "license": "MIT",
6
+ "repository": "https://github.com/D4rkr34lm/declarative-server",
7
+ "exports": {
8
+ "./server": {
9
+ "import": "./dist/server/index.js",
10
+ "require": "./dist/server/index.js",
11
+ "types": "./dist/server/server.d.ts"
12
+ }
13
+ },
14
+ "bin": "./dist/cli/cli.js",
15
+ "scripts": {
16
+ "lint": "eslint",
17
+ "test": "vitest",
18
+ "build": "rm -rf ./dist && tsc"
19
+ },
20
+ "devDependencies": {
21
+ "@types/cookie-parser": "^1.4.9",
22
+ "@types/express": "^5.0.0",
23
+ "@vitest/coverage-v8": "^4.0.15",
24
+ "@vitest/eslint-plugin": "^1.5.2",
25
+ "eslint": "^9.39.1",
26
+ "eslint-config-prettier": "^10.1.8",
27
+ "eslint-plugin-prettier": "^5.5.4",
28
+ "jiti": "^2.6.1",
29
+ "prettier": "^3.7.4",
30
+ "tslib": "2.8.1",
31
+ "typescript": "^5.9.3",
32
+ "typescript-eslint": "^8.49.0",
33
+ "vite-tsconfig-paths": "^5.1.4",
34
+ "vitest": "^4.0.15"
35
+ },
36
+ "dependencies": {
37
+ "colors": "^1.4.0",
38
+ "commander": "^14.0.2",
39
+ "cookie-parser": "^1.4.7",
40
+ "express": "^4.21.1",
41
+ "express-async-handler": "^1.2.0",
42
+ "glob": "^13.0.0",
43
+ "openapi-types": "^12.1.3",
44
+ "zod": "^4.1.13"
45
+ }
46
+ }
@@ -0,0 +1,14 @@
1
+ /**
2
+ * @see https://prettier.io/docs/configuration
3
+ * @type {import("prettier").Config}
4
+ */
5
+ const config = {
6
+ trailingComma: "all",
7
+ tabWidth: 2,
8
+ semi: true,
9
+ singleQuote: false,
10
+ printWidth: 80,
11
+ endOfLine: "lf",
12
+ };
13
+
14
+ export default config;
package/src/cli/cli.ts ADDED
@@ -0,0 +1,37 @@
1
+ import { Command } from "commander";
2
+ import fs from "fs";
3
+ import { generateOpenApiDoc } from "./generateOpenApi";
4
+
5
+ const program = new Command();
6
+
7
+ program
8
+ .name("transit-kit")
9
+ .description("CLI of the transitKit backend framework")
10
+
11
+ .version("1.0.0");
12
+
13
+ program
14
+ .command("generate-openapi")
15
+ .option(
16
+ "-o, --output <path>",
17
+
18
+ "Output path for the generated OpenAPI document",
19
+ "openapi.json",
20
+ )
21
+ .option(
22
+ "-t, --target <path>",
23
+ "Target path to search for endpoint definitions",
24
+ ".",
25
+ )
26
+ .action(async (options) => {
27
+ const { output, target } = options;
28
+
29
+ const generatedDoc = await generateOpenApiDoc(target);
30
+
31
+ fs.writeFileSync(output, JSON.stringify(generatedDoc, null, 2), {
32
+ encoding: "utf-8",
33
+ });
34
+ console.log(`OpenAPI document generated at: ${output}`);
35
+ });
36
+
37
+ program.parse();
@@ -0,0 +1,217 @@
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
+ }
@@ -0,0 +1,11 @@
1
+ export const HttpMethods = {
2
+ get: "get",
3
+ post: "post",
4
+ put: "put",
5
+ delete: "delete",
6
+ patch: "patch",
7
+ head: "head",
8
+ options: "options",
9
+ } as const;
10
+
11
+ export type HttpMethod = (typeof HttpMethods)[keyof typeof HttpMethods];
@@ -0,0 +1,46 @@
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];
@@ -0,0 +1,24 @@
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
+ };
@@ -0,0 +1,24 @@
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
+ >;