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.
Files changed (41) hide show
  1. package/dist/cli/cli.js +3 -2
  2. package/dist/cli/generateOpenApi.d.ts +1 -1
  3. package/dist/cli/generateOpenApi.js +35 -57
  4. package/dist/server/handlers/api/createApiHandler.d.ts +1 -1
  5. package/dist/server/handlers/api/createApiHandler.js +1 -1
  6. package/dist/server/index.d.ts +1 -1
  7. package/dist/server/server.d.ts +12 -11
  8. package/dist/server/server.js +23 -22
  9. package/package.json +24 -3
  10. package/.github/workflows/ci.yml +0 -73
  11. package/.github/workflows/release.yml +0 -37
  12. package/eslint-configs/eslint.base.config.js +0 -30
  13. package/eslint-configs/eslint.node.config.js +0 -23
  14. package/eslint-configs/eslint.test.config.js +0 -15
  15. package/eslint.config.ts +0 -6
  16. package/prettier.config.js +0 -14
  17. package/src/cli/cli.ts +0 -37
  18. package/src/cli/generateOpenApi.ts +0 -217
  19. package/src/server/constants/HttpMethods.ts +0 -11
  20. package/src/server/constants/HttpStatusCodes.ts +0 -46
  21. package/src/server/handlers/api/EndpointDefinition.ts +0 -24
  22. package/src/server/handlers/api/EndpointHandler.ts +0 -24
  23. package/src/server/handlers/api/HandlerFormDefinition.spec.ts +0 -120
  24. package/src/server/handlers/api/HandlerFromDefinition.ts +0 -33
  25. package/src/server/handlers/api/PathParameters.spec.ts +0 -28
  26. package/src/server/handlers/api/PathParameters.ts +0 -10
  27. package/src/server/handlers/api/createApiHandler.ts +0 -44
  28. package/src/server/handlers/api/responses/emptyResponse.ts +0 -12
  29. package/src/server/handlers/api/responses/index.ts +0 -15
  30. package/src/server/handlers/api/responses/jsonResponse.ts +0 -44
  31. package/src/server/index.ts +0 -4
  32. package/src/server/middleware/logging.ts +0 -41
  33. package/src/server/middleware/validation.ts +0 -35
  34. package/src/server/server.ts +0 -90
  35. package/src/server/utils/funcs.ts +0 -3
  36. package/src/server/utils/logging.ts +0 -10
  37. package/src/server/utils/typeGuards.ts +0 -9
  38. package/src/server/utils/types.ts +0 -3
  39. package/transitKit.code-workspace +0 -36
  40. package/tsconfig.json +0 -15
  41. package/vitest.config.ts +0 -22
package/dist/cli/cli.js CHANGED
@@ -9,9 +9,10 @@ program
9
9
  program
10
10
  .command("generate-openapi")
11
11
  .option("-o, --output <path>", "Output path for the generated OpenAPI document", "openapi.json")
12
+ .option("-t, --target <path>", "Target path to search for endpoint definitions", ".")
12
13
  .action(async (options) => {
13
- const { output } = options;
14
- const generatedDoc = await generateOpenApiDoc();
14
+ const { output, target } = options;
15
+ const generatedDoc = await generateOpenApiDoc(target);
15
16
  fs.writeFileSync(output, JSON.stringify(generatedDoc, null, 2), {
16
17
  encoding: "utf-8",
17
18
  });
@@ -1,2 +1,2 @@
1
1
  import { OpenAPIV3 } from "openapi-types";
2
- export declare function generateOpenApiDoc(): Promise<OpenAPIV3.Document<{}>>;
2
+ export declare function generateOpenApiDoc(targetPath: string): Promise<OpenAPIV3.Document<{}>>;
@@ -1,38 +1,7 @@
1
- import { glob } from "glob";
2
- import path from "path";
3
1
  import z from "zod";
4
- import { hasNoValue, hasValue } from "../server/utils/typeGuards";
2
+ import { hasValue } from "../server/utils/typeGuards";
3
+ import path from "path";
5
4
  import { isJsonResponseSchema } from "../server/handlers/api/responses/jsonResponse";
6
- async function findEndpointDefinitions() {
7
- const files = await glob("**/*.ts", {
8
- cwd: process.cwd(),
9
- ignore: ["**/node_modules/**", "**/*.spec.ts"],
10
- });
11
- const definitions = await Promise.all(files.map(async (file) => {
12
- const absolutePath = path.resolve(file);
13
- const fileUrl = path.toNamespacedPath(absolutePath).startsWith("/")
14
- ? `file://${absolutePath}`
15
- : `file:///${absolutePath}`;
16
- try {
17
- const module = await import(fileUrl);
18
- if (module.default) {
19
- const def = module.default.__API_ENDPOINT_DEFINITION__;
20
- if (hasNoValue(def)) {
21
- return null;
22
- }
23
- return def;
24
- }
25
- else {
26
- return null;
27
- }
28
- }
29
- catch (error) {
30
- console.error(`Error importing ${file}:`, error);
31
- return null;
32
- }
33
- }));
34
- return definitions.filter(hasValue);
35
- }
36
5
  function extractPathAndParameters(path) {
37
6
  const parameters = path.match(/:([a-zA-Z0-9_]+)/g)?.map((param) => {
38
7
  return {
@@ -125,28 +94,37 @@ function translateToOpenAPIPathItem(definition) {
125
94
  };
126
95
  return [openApiPath, pathItem];
127
96
  }
128
- export async function generateOpenApiDoc() {
129
- const definitions = await findEndpointDefinitions();
130
- const paths = definitions.reduce((acc, def) => {
131
- const [openApiPath, pathItem] = translateToOpenAPIPathItem(def);
132
- if (acc[openApiPath]) {
133
- acc[openApiPath] = {
134
- ...acc[openApiPath],
135
- ...pathItem,
136
- };
137
- }
138
- else {
139
- acc[openApiPath] = pathItem;
140
- }
141
- return acc;
142
- }, {});
143
- const openApiDocument = {
144
- openapi: "3.0.0",
145
- info: {
146
- title: "Generated API",
147
- version: "1.0.0",
148
- },
149
- paths: paths,
150
- };
151
- return openApiDocument;
97
+ export async function generateOpenApiDoc(targetPath) {
98
+ const serverModule = await import(path.resolve(process.cwd(), targetPath));
99
+ const server = serverModule.default;
100
+ if (hasValue(server) &&
101
+ hasValue(server.endpointDefinitions) &&
102
+ Array.isArray(server.endpointDefinitions)) {
103
+ const endpointDefinitions = server.endpointDefinitions;
104
+ const paths = endpointDefinitions.reduce((acc, def) => {
105
+ const [openApiPath, pathItem] = translateToOpenAPIPathItem(def);
106
+ if (acc[openApiPath]) {
107
+ acc[openApiPath] = {
108
+ ...acc[openApiPath],
109
+ ...pathItem,
110
+ };
111
+ }
112
+ else {
113
+ acc[openApiPath] = pathItem;
114
+ }
115
+ return acc;
116
+ }, {});
117
+ const openApiDocument = {
118
+ openapi: "3.0.0",
119
+ info: {
120
+ title: "Generated API",
121
+ version: "1.0.0",
122
+ },
123
+ paths: paths,
124
+ };
125
+ return openApiDocument;
126
+ }
127
+ else {
128
+ throw new Error("The specified module does not export a valid server instance.");
129
+ }
152
130
  }
@@ -5,7 +5,7 @@ import { ApiEndpointHandler } from "./EndpointHandler";
5
5
  import { HandlerForDefinition } from "./HandlerFromDefinition";
6
6
  import { GenericResponseSchemaMap } from "./responses";
7
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>, handler: HandlerForDefinition<Path, RequestBody, Query, ResponsesMap>): {
8
- __API_ENDPOINT_DEFINITION__: ApiEndpointDefinition<Path, Method, RequestBody, Query, ResponsesMap>;
8
+ type: string;
9
9
  definition: ApiEndpointDefinition<Path, Method, RequestBody, Query, ResponsesMap>;
10
10
  handler: HandlerForDefinition<Path, RequestBody, Query, ResponsesMap>;
11
11
  };
@@ -2,7 +2,7 @@ import expressAsyncHandler from "express-async-handler";
2
2
  import { isJsonResponse } from "./responses/jsonResponse";
3
3
  export function createApiEndpointHandler(definition, handler) {
4
4
  return {
5
- __API_ENDPOINT_DEFINITION__: definition,
5
+ type: "__API_ENDPOINT_DEFINITION__",
6
6
  definition,
7
7
  handler,
8
8
  };
@@ -1,4 +1,4 @@
1
1
  export { createApiEndpointHandler } from "./handlers/api/createApiHandler";
2
- export { createServer, type Server, type ServerOptions } from "./server";
2
+ export { createServer, type Server, type ServerConfig } from "./server";
3
3
  declare const _default: {};
4
4
  export default _default;
@@ -4,18 +4,19 @@ import { HttpMethod } from "./constants/HttpMethods";
4
4
  import { ApiEndpointDefinition } from "./handlers/api/EndpointDefinition";
5
5
  import { ApiEndpointHandler } from "./handlers/api/EndpointHandler";
6
6
  import { Logger } from "./utils/logging";
7
- export interface ServerOptions {
8
- inDevMode?: boolean;
9
- port?: number;
10
- logger?: Logger | boolean;
7
+ export interface ServerConfig {
8
+ inDevMode: boolean;
9
+ port: number;
10
+ logger: Logger | boolean;
11
+ endpoints: Array<{
12
+ endpointHandler: ApiEndpointHandler;
13
+ endpointDefinition: ApiEndpointDefinition<string, HttpMethod, z.ZodType, z.ZodType, {}>;
14
+ }>;
11
15
  }
12
16
  export interface Server {
13
- _expressApp: Application;
14
- _logger: Logger | boolean;
17
+ expressApp: Application;
18
+ logger: Logger | boolean;
19
+ endpointDefinitions: ApiEndpointDefinition<string, HttpMethod, z.ZodType, z.ZodType, {}>[];
15
20
  start: () => void;
16
- registerApiEndpoint: (endpoint: {
17
- endpointHandler: ApiEndpointHandler;
18
- endpointDefinition: ApiEndpointDefinition<string, HttpMethod, z.ZodType, z.ZodType, {}>;
19
- }) => void;
20
21
  }
21
- export declare function createServer(options: ServerOptions): Server;
22
+ export declare function createServer(config: ServerConfig): Server;
@@ -5,39 +5,40 @@ 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
- export function createServer(options) {
9
- const { port = 3000, inDevMode = false } = options;
10
- const logger = options.logger === true
8
+ function registerApiEndpoint(expressApp, endpointDefinition, endpointHandler) {
9
+ const handlerStack = [
10
+ hasValue(endpointDefinition.querySchema)
11
+ ? buildQueryValidatorMiddleware(endpointDefinition.querySchema)
12
+ : null,
13
+ hasValue(endpointDefinition.requestBodySchema)
14
+ ? buildBodyValidatorMiddleware(endpointDefinition.requestBodySchema)
15
+ : null,
16
+ buildApiEndpointHandler(endpointHandler),
17
+ ].filter(hasValue);
18
+ expressApp[endpointDefinition.method](endpointDefinition.path, handlerStack);
19
+ }
20
+ export function createServer(config) {
21
+ const { port, inDevMode, endpoints } = config;
22
+ const logger = config.logger === true
11
23
  ? console
12
- : options.logger === false || hasNoValue(options.logger)
24
+ : config.logger === false || hasNoValue(config.logger)
13
25
  ? NoOpLogger
14
- : options.logger;
26
+ : config.logger;
15
27
  const app = express();
16
28
  app.use(express.json());
17
29
  app.use(express.urlencoded({ extended: true }));
18
30
  app.use(cookieParser());
19
31
  app.use(buildRequestLogger(logger, inDevMode));
20
32
  app.use(buildResponseLogger(logger, inDevMode));
33
+ endpoints.forEach(({ endpointDefinition, endpointHandler }) => {
34
+ registerApiEndpoint(app, endpointDefinition, endpointHandler);
35
+ });
21
36
  return {
22
- _expressApp: app,
23
- _logger: logger,
37
+ expressApp: app,
38
+ logger: logger,
39
+ endpointDefinitions: endpoints.map((e) => e.endpointDefinition),
24
40
  start: () => {
25
41
  app.listen(port);
26
42
  },
27
- registerApiEndpoint: ({ endpointDefinition, endpointHandler }) => {
28
- registerApiEndpoint(app, endpointDefinition, endpointHandler);
29
- },
30
43
  };
31
44
  }
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
- }
package/package.json CHANGED
@@ -1,9 +1,31 @@
1
1
  {
2
2
  "name": "transit-kit",
3
- "version": "0.1.0",
3
+ "version": "0.2.0",
4
+ "description": "A declarative TypeScript framework for building type-safe Express.js APIs with automatic OpenAPI generation",
5
+ "keywords": [
6
+ "express",
7
+ "typescript",
8
+ "api",
9
+ "openapi",
10
+ "zod",
11
+ "rest",
12
+ "server",
13
+ "framework"
14
+ ],
4
15
  "type": "module",
5
16
  "license": "MIT",
6
- "repository": "https://github.com/D4rkr34lm/declarative-server",
17
+ "author": "D4rkr34lm",
18
+ "repository": {
19
+ "type": "git",
20
+ "url": "https://github.com/D4rkr34lm/transit-kit.git"
21
+ },
22
+ "homepage": "https://github.com/D4rkr34lm/transit-kit#readme",
23
+ "bugs": {
24
+ "url": "https://github.com/D4rkr34lm/transit-kit/issues"
25
+ },
26
+ "files": [
27
+ "dist"
28
+ ],
7
29
  "exports": {
8
30
  "./server": {
9
31
  "import": "./dist/server/index.js",
@@ -39,7 +61,6 @@
39
61
  "cookie-parser": "^1.4.7",
40
62
  "express": "^4.21.1",
41
63
  "express-async-handler": "^1.2.0",
42
- "glob": "^13.0.0",
43
64
  "openapi-types": "^12.1.3",
44
65
  "zod": "^4.1.13"
45
66
  }
@@ -1,73 +0,0 @@
1
- name: CI
2
-
3
- on:
4
- push:
5
- branches: [main, develop]
6
- pull_request:
7
- branches: [main, develop]
8
-
9
- jobs:
10
- lint:
11
- name: ๐Ÿ” Lint
12
- runs-on: ubuntu-latest
13
- steps:
14
- - name: Checkout code
15
- uses: actions/checkout@v4
16
-
17
- - name: Setup Node.js
18
- uses: actions/setup-node@v4
19
- with:
20
- node-version: "20"
21
- cache: "npm"
22
-
23
- - name: ๐Ÿ“ฆ Install dependencies
24
- run: npm ci
25
-
26
- - name: โœจ Run linter
27
- run: npm run lint
28
-
29
- test:
30
- name: ๐Ÿงช Test
31
- runs-on: ubuntu-latest
32
- steps:
33
- - name: ๐Ÿ“ฅ Checkout code
34
- uses: actions/checkout@v4
35
-
36
- - name: โš™๏ธ Setup Node.js
37
- uses: actions/setup-node@v4
38
- with:
39
- node-version: "22"
40
- cache: "npm"
41
-
42
- - name: ๐Ÿ“ฆ Install dependencies
43
- run: npm ci
44
-
45
- - name: ๐Ÿงช Run tests
46
- run: npm run test -- --run
47
-
48
- build:
49
- name: ๐Ÿ—๏ธ Build
50
- runs-on: ubuntu-latest
51
- needs: [lint, test]
52
- steps:
53
- - name: ๐Ÿ“ฅ Checkout code
54
- uses: actions/checkout@v4
55
-
56
- - name: โš™๏ธ Setup Node.js
57
- uses: actions/setup-node@v4
58
- with:
59
- node-version: "20"
60
- cache: "npm"
61
-
62
- - name: ๐Ÿ“ฆ Install dependencies
63
- run: npm ci
64
-
65
- - name: ๐Ÿ”จ Build package
66
- run: npm run build
67
-
68
- - name: ๐Ÿ“ค Upload build artifacts
69
- uses: actions/upload-artifact@v4
70
- with:
71
- name: dist
72
- path: dist/
73
- retention-days: 7
@@ -1,37 +0,0 @@
1
- name: Release
2
-
3
- on:
4
- release:
5
- types: [published]
6
-
7
- jobs:
8
- publish:
9
- name: ๐Ÿš€ Publish to npm
10
- runs-on: ubuntu-latest
11
- permissions:
12
- contents: read
13
- id-token: write
14
- steps:
15
- - name: ๐Ÿ“ฅ Checkout code
16
- uses: actions/checkout@v4
17
-
18
- - name: โš™๏ธ Setup Node.js
19
- uses: actions/setup-node@v4
20
- with:
21
- node-version: "20"
22
- registry-url: "https://registry.npmjs.org"
23
- cache: "npm"
24
-
25
- - name: ๐Ÿ“ฆ Install dependencies
26
- run: npm ci
27
-
28
- - name: ๐Ÿงช Run tests
29
- run: npm run test -- --run
30
-
31
- - name: ๐Ÿ”จ Build package
32
- run: npm run build
33
-
34
- - name: ๐Ÿš€ Publish to npm
35
- run: npm publish --provenance
36
- env:
37
- NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
@@ -1,30 +0,0 @@
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
- );
@@ -1,23 +0,0 @@
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
- });
@@ -1,15 +0,0 @@
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
- });
package/eslint.config.ts DELETED
@@ -1,6 +0,0 @@
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);
@@ -1,14 +0,0 @@
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 DELETED
@@ -1,37 +0,0 @@
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();