inviton-backduck 1.0.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 (99) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +302 -0
  3. package/dist/apidoc/api-doc-generator.d.ts +58 -0
  4. package/dist/apidoc/api-doc-generator.d.ts.map +1 -0
  5. package/dist/apidoc/api-doc-generator.js +201 -0
  6. package/dist/apidoc/api-doc-generator.js.map +1 -0
  7. package/dist/apidoc/config.d.ts +153 -0
  8. package/dist/apidoc/config.d.ts.map +1 -0
  9. package/dist/apidoc/config.js +254 -0
  10. package/dist/apidoc/config.js.map +1 -0
  11. package/dist/apidoc/controller-parser.d.ts +208 -0
  12. package/dist/apidoc/controller-parser.d.ts.map +1 -0
  13. package/dist/apidoc/controller-parser.js +686 -0
  14. package/dist/apidoc/controller-parser.js.map +1 -0
  15. package/dist/apidoc/html-generator.d.ts +290 -0
  16. package/dist/apidoc/html-generator.d.ts.map +1 -0
  17. package/dist/apidoc/html-generator.js +2295 -0
  18. package/dist/apidoc/html-generator.js.map +1 -0
  19. package/dist/apidoc/index.d.ts +20 -0
  20. package/dist/apidoc/index.d.ts.map +1 -0
  21. package/dist/apidoc/index.js +16 -0
  22. package/dist/apidoc/index.js.map +1 -0
  23. package/dist/apidoc/openapi-builder.d.ts +169 -0
  24. package/dist/apidoc/openapi-builder.d.ts.map +1 -0
  25. package/dist/apidoc/openapi-builder.js +634 -0
  26. package/dist/apidoc/openapi-builder.js.map +1 -0
  27. package/dist/apidoc/parameterGeneratorRegistry.d.ts +20 -0
  28. package/dist/apidoc/parameterGeneratorRegistry.d.ts.map +1 -0
  29. package/dist/apidoc/parameterGeneratorRegistry.js +6 -0
  30. package/dist/apidoc/parameterGeneratorRegistry.js.map +1 -0
  31. package/dist/apidoc/test-type-resolver.d.ts +2 -0
  32. package/dist/apidoc/test-type-resolver.d.ts.map +1 -0
  33. package/dist/apidoc/test-type-resolver.js +6 -0
  34. package/dist/apidoc/test-type-resolver.js.map +1 -0
  35. package/dist/apidoc/type-resolver.d.ts +266 -0
  36. package/dist/apidoc/type-resolver.d.ts.map +1 -0
  37. package/dist/apidoc/type-resolver.js +1226 -0
  38. package/dist/apidoc/type-resolver.js.map +1 -0
  39. package/dist/apidoc/verify-type-resolution.d.ts +3 -0
  40. package/dist/apidoc/verify-type-resolution.d.ts.map +1 -0
  41. package/dist/apidoc/verify-type-resolution.js +29 -0
  42. package/dist/apidoc/verify-type-resolution.js.map +1 -0
  43. package/dist/bun/bunRouter.d.ts +70 -0
  44. package/dist/bun/bunRouter.d.ts.map +1 -0
  45. package/dist/bun/bunRouter.js +324 -0
  46. package/dist/bun/bunRouter.js.map +1 -0
  47. package/dist/bun/bunServer.d.ts +72 -0
  48. package/dist/bun/bunServer.d.ts.map +1 -0
  49. package/dist/bun/bunServer.js +218 -0
  50. package/dist/bun/bunServer.js.map +1 -0
  51. package/dist/bun/bunStaticFiles.d.ts +76 -0
  52. package/dist/bun/bunStaticFiles.d.ts.map +1 -0
  53. package/dist/bun/bunStaticFiles.js +251 -0
  54. package/dist/bun/bunStaticFiles.js.map +1 -0
  55. package/dist/bun/index.d.ts +7 -0
  56. package/dist/bun/index.d.ts.map +1 -0
  57. package/dist/bun/index.js +7 -0
  58. package/dist/bun/index.js.map +1 -0
  59. package/dist/data-contracts.d.ts +132 -0
  60. package/dist/data-contracts.d.ts.map +1 -0
  61. package/dist/data-contracts.js +2 -0
  62. package/dist/data-contracts.js.map +1 -0
  63. package/dist/decorators.d.ts +75 -0
  64. package/dist/decorators.d.ts.map +1 -0
  65. package/dist/decorators.js +101 -0
  66. package/dist/decorators.js.map +1 -0
  67. package/dist/express/expressFrontendRouter.d.ts +17 -0
  68. package/dist/express/expressFrontendRouter.d.ts.map +1 -0
  69. package/dist/express/expressFrontendRouter.js +33 -0
  70. package/dist/express/expressFrontendRouter.js.map +1 -0
  71. package/dist/express/expressRouter.d.ts +25 -0
  72. package/dist/express/expressRouter.d.ts.map +1 -0
  73. package/dist/express/expressRouter.js +150 -0
  74. package/dist/express/expressRouter.js.map +1 -0
  75. package/dist/express/index.d.ts +6 -0
  76. package/dist/express/index.d.ts.map +1 -0
  77. package/dist/express/index.js +6 -0
  78. package/dist/express/index.js.map +1 -0
  79. package/dist/index.d.ts +47 -0
  80. package/dist/index.d.ts.map +1 -0
  81. package/dist/index.js +52 -0
  82. package/dist/index.js.map +1 -0
  83. package/dist/router.d.ts +162 -0
  84. package/dist/router.d.ts.map +1 -0
  85. package/dist/router.js +350 -0
  86. package/dist/router.js.map +1 -0
  87. package/dist/runtime-detect.d.ts +20 -0
  88. package/dist/runtime-detect.d.ts.map +1 -0
  89. package/dist/runtime-detect.js +20 -0
  90. package/dist/runtime-detect.js.map +1 -0
  91. package/dist/server.d.ts +126 -0
  92. package/dist/server.d.ts.map +1 -0
  93. package/dist/server.js +181 -0
  94. package/dist/server.js.map +1 -0
  95. package/dist/utils.d.ts +83 -0
  96. package/dist/utils.d.ts.map +1 -0
  97. package/dist/utils.js +157 -0
  98. package/dist/utils.js.map +1 -0
  99. package/package.json +65 -0
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Inviton
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,302 @@
1
+ # @inviton/backduck
2
+
3
+ Runtime-agnostic utilities for building web servers with Express and Bun support.
4
+
5
+ Write your server code once, and it will automatically adapt to the runtime environment (Node.js with Express or Bun native).
6
+
7
+ ## Features
8
+
9
+ - **Universal Router**: Define routes once, run anywhere
10
+ - **Controller-based routing** with automatic REST mapping
11
+ - **Runtime detection** and automatic adaptation
12
+ - **Type-safe** request/response handling
13
+ - **Middleware support** with chaining
14
+ - **Static file serving** with compression
15
+ - **API Documentation Generation**: Automatic OpenAPI/Swagger and HTML docs from TypeScript controllers
16
+
17
+ ## Installation
18
+
19
+ ```bash
20
+ npm install @inviton/backduck
21
+ ```
22
+
23
+ ### Peer Dependencies
24
+
25
+ Depending on your runtime, install the appropriate peer dependencies:
26
+
27
+ **For Node.js/Express:**
28
+ ```bash
29
+ npm install express
30
+ ```
31
+
32
+ **For Bun:**
33
+ ```bash
34
+ # Bun automatically provides bun-types
35
+ ```
36
+
37
+ ## Quick Start
38
+
39
+ ### 1. Define a Controller
40
+
41
+ ```typescript
42
+ import { Route, ControllerBase } from '@inviton/backduck';
43
+
44
+ interface CreateProductArgs {
45
+ name: string;
46
+ price: number;
47
+ }
48
+
49
+ @Route('/products')
50
+ export class ProductController extends ControllerBase {
51
+ // GET /products
52
+ async getAll() {
53
+ return { products: [] };
54
+ }
55
+
56
+ // GET /products/:id
57
+ async get(args: { id: number }) {
58
+ return { id: args.id, name: 'Product' };
59
+ }
60
+
61
+ // POST /products
62
+ async post(args: CreateProductArgs) {
63
+ return { id: 1, ...args };
64
+ }
65
+
66
+ // PUT /products/:id
67
+ async put(args: { id: number } & CreateProductArgs) {
68
+ return { id: args.id, name: args.name, price: args.price };
69
+ }
70
+
71
+ // DELETE /products/:id
72
+ async delete(args: { id: number }) {
73
+ return { success: true };
74
+ }
75
+ }
76
+ ```
77
+
78
+ ### 2. Create Router and Register Controllers
79
+
80
+ ```typescript
81
+ import { createRouter } from '@inviton/backduck';
82
+ import { ProductController } from './controllers/ProductController';
83
+
84
+ const router = createRouter();
85
+ router.registerController(ProductController);
86
+ ```
87
+
88
+ ### 3. Create and Start Server
89
+
90
+ ```typescript
91
+ import { createServer } from '@inviton/backduck';
92
+
93
+ const server = createServer({
94
+ port: 3000,
95
+ apiRouters: {
96
+ '/api': router
97
+ }
98
+ });
99
+
100
+ await server.start();
101
+ ```
102
+
103
+ That's it! Your server will automatically use Bun's native server if running under Bun, or Express if running under Node.js.
104
+
105
+ ## API Documentation Generation
106
+
107
+ Generate OpenAPI/Swagger and HTML documentation from your TypeScript controllers:
108
+
109
+ ### 1. Create Configuration
110
+
111
+ ```typescript
112
+ // apidoc-config.ts
113
+ import type { ApiDocConfig as IApiDocConfig } from '@inviton/backduck';
114
+
115
+ export const ApiDocConfig: typeof IApiDocConfig = {
116
+ info: {
117
+ title: 'My API',
118
+ version: '1.0.0',
119
+ description: 'API documentation',
120
+ },
121
+
122
+ getSourcePath: (serverDir, apiType) =>
123
+ path.join(serverDir, 'src', 'api', apiType === 'shopApi' ? 'shop' : 'admin'),
124
+
125
+ getOutputPath: (serverDir, format) =>
126
+ path.join(serverDir, 'docs', format),
127
+
128
+ output: {
129
+ specFilename: 'openapi.json',
130
+ },
131
+
132
+ patterns: {
133
+ controllerFiles: /Controller\.ts$/,
134
+ },
135
+
136
+ getHttpMethod: (methodName) => {
137
+ const map: Record<string, string> = {
138
+ getAll: 'GET',
139
+ get: 'GET',
140
+ post: 'POST',
141
+ put: 'PUT',
142
+ patch: 'PATCH',
143
+ delete: 'DELETE',
144
+ };
145
+ return map[methodName] || 'GET';
146
+ },
147
+
148
+ getCurrentServerUrl: () => process.env.API_URL || 'http://localhost:3000',
149
+ };
150
+ ```
151
+
152
+ ### 2. Generate Documentation
153
+
154
+ ```typescript
155
+ import { ApiDocGenerator } from '@inviton/backduck';
156
+ import { ApiDocConfig } from './apidoc-config';
157
+
158
+ const result = await ApiDocGenerator.generate({
159
+ config: ApiDocConfig,
160
+ serverDir: __dirname,
161
+ verbose: true,
162
+ });
163
+
164
+ console.log(`Generated ${result.endpointsCount} endpoints from ${result.controllersCount} controllers`);
165
+ ```
166
+
167
+ ## Decorators
168
+
169
+ ### @Route(path: string)
170
+
171
+ Define the base route path for a controller:
172
+
173
+ ```typescript
174
+ @Route('/users')
175
+ class UserController extends ControllerBase {
176
+ // Routes will be /users, /users/:id, etc.
177
+ }
178
+ ```
179
+
180
+ ### @Middleware(middleware)
181
+
182
+ Add middleware to a controller or method:
183
+
184
+ ```typescript
185
+ @Route('/admin')
186
+ @Middleware(authMiddleware)
187
+ class AdminController extends ControllerBase {
188
+ // All methods require authentication
189
+
190
+ @Middleware(adminOnlyMiddleware)
191
+ async delete(args: { id: number }) {
192
+ // This method requires both auth and admin
193
+ }
194
+ }
195
+ ```
196
+
197
+ ### @ParseRequestArgs(parser)
198
+
199
+ Custom request argument parsing:
200
+
201
+ ```typescript
202
+ @Route('/search')
203
+ class SearchController extends ControllerBase {
204
+ @ParseRequestArgs((req) => ({
205
+ query: req.query.q as string,
206
+ page: parseInt(req.query.page as string) || 1,
207
+ }))
208
+ async get(args: { query: string; page: number }) {
209
+ return { results: [], page: args.page };
210
+ }
211
+ }
212
+ ```
213
+
214
+ ### @RequestSize(maxSizeInMB)
215
+
216
+ Set maximum request body size:
217
+
218
+ ```typescript
219
+ @Route('/upload')
220
+ @RequestSize(50) // Allow up to 50MB
221
+ class UploadController extends ControllerBase {
222
+ async post(args: { file: Buffer }) {
223
+ // Handle large file upload
224
+ }
225
+ }
226
+ ```
227
+
228
+ ## Runtime Utilities
229
+
230
+ The `RuntimeUtils` class provides helper methods for request parsing:
231
+
232
+ ```typescript
233
+ import { RuntimeUtils, type WebRequest } from '@inviton/backduck';
234
+
235
+ // Parse query parameters
236
+ const id = RuntimeUtils.parseQueryNumber(req, 'id');
237
+ const tags = RuntimeUtils.parseQueryStringArray(req, 'tags');
238
+ const active = RuntimeUtils.parseQueryBoolean(req, 'active');
239
+ const filters = RuntimeUtils.parseQueryJSON(req, 'filters');
240
+
241
+ // Get client IP (handles proxies)
242
+ const ip = RuntimeUtils.getIpAddress(req);
243
+ ```
244
+
245
+ ## HTTP Method Mapping
246
+
247
+ Controller methods are automatically mapped to HTTP methods:
248
+
249
+ | Method Name | HTTP Method | Route Pattern | Example |
250
+ |------------|-------------|---------------|---------|
251
+ | `getAll()` | GET | `/` | `GET /products` |
252
+ | `get(args)` | GET | `/:id` | `GET /products/123` |
253
+ | `post(args)` | POST | `/` | `POST /products` |
254
+ | `put(args)` | PUT | `/:id` | `PUT /products/123` |
255
+ | `patch(args)` | PATCH | `/:id` | `PATCH /products/123` |
256
+ | `delete(args)` | DELETE | `/:id` | `DELETE /products/123` |
257
+
258
+ Custom method names can be configured via the `ApiDocConfig.getHttpMethod()` function.
259
+
260
+ ## Static File Serving
261
+
262
+ Serve static files with automatic compression:
263
+
264
+ ```typescript
265
+ import { createServer } from '@inviton/backduck';
266
+
267
+ const server = createServer({
268
+ port: 3000,
269
+ apiRouters: { '/api': router },
270
+ staticFiles: [
271
+ {
272
+ urlPath: '/assets',
273
+ fsPath: './public/assets',
274
+ cacheControl: 'public, max-age=31536000', // 1 year
275
+ },
276
+ ],
277
+ });
278
+ ```
279
+
280
+ ## Runtime Detection
281
+
282
+ ```typescript
283
+ import { RUNTIME, IS_BUN } from '@inviton/backduck';
284
+
285
+ if (IS_BUN) {
286
+ console.log('Running on Bun!');
287
+ } else {
288
+ console.log('Running on Node.js with Express');
289
+ }
290
+ ```
291
+
292
+ ## License
293
+
294
+ MIT
295
+
296
+ ## Author
297
+
298
+ Inviton
299
+
300
+ ## Repository
301
+
302
+ https://github.com/inviton/backduck
@@ -0,0 +1,58 @@
1
+ /**
2
+ * API Documentation Generator
3
+ * Main class for generating OpenAPI and HTML documentation from TypeScript controllers
4
+ */
5
+ import type { ApiDocConfig } from './config';
6
+ /**
7
+ * Options for API documentation generation
8
+ */
9
+ export interface ApiDocGeneratorOptions {
10
+ /** Configuration instance implementing ApiDocConfig interface */
11
+ config: typeof ApiDocConfig;
12
+ /** Server root directory path */
13
+ serverDir: string;
14
+ /** Run in dry-run mode (don't write files) */
15
+ dryRun?: boolean;
16
+ /** Verbose output */
17
+ verbose?: boolean;
18
+ /** Only generate OpenAPI spec */
19
+ openApiOnly?: boolean;
20
+ /** Only generate HTML docs */
21
+ htmlOnly?: boolean;
22
+ /** Common type directories to pre-load for faster resolution */
23
+ commonTypeDirs?: string[];
24
+ }
25
+ /**
26
+ * Result of API documentation generation
27
+ */
28
+ export interface ApiDocGenerationResult {
29
+ /** Number of controllers processed */
30
+ controllersCount: number;
31
+ /** Number of endpoints generated */
32
+ endpointsCount: number;
33
+ /** Number of unique types resolved */
34
+ typesCount: number;
35
+ /** Total generation time in milliseconds */
36
+ totalTime: number;
37
+ /** OpenAPI specification (if generated) */
38
+ openApiSpec?: any;
39
+ /** HTML documentation (if generated) */
40
+ htmlDoc?: string;
41
+ }
42
+ /**
43
+ * API Documentation Generator
44
+ * Generates OpenAPI and HTML documentation from TypeScript controllers
45
+ */
46
+ export declare class ApiDocGenerator {
47
+ /**
48
+ * Generate API documentation
49
+ * @param options - Generation options with config and paths
50
+ * @returns Generation result with statistics
51
+ */
52
+ static generate(options: ApiDocGeneratorOptions): Promise<ApiDocGenerationResult>;
53
+ private static controllerToEndpointData;
54
+ private static controllerToHtmlEndpointData;
55
+ private static ensureDir;
56
+ private static cleanHtmlDir;
57
+ }
58
+ //# sourceMappingURL=api-doc-generator.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"api-doc-generator.d.ts","sourceRoot":"","sources":["../../src/apidoc/api-doc-generator.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAGH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,UAAU,CAAC;AAa7C;;GAEG;AACH,MAAM,WAAW,sBAAsB;IACtC,iEAAiE;IACjE,MAAM,EAAE,OAAO,YAAY,CAAC;IAC5B,iCAAiC;IACjC,SAAS,EAAE,MAAM,CAAC;IAClB,8CAA8C;IAC9C,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,qBAAqB;IACrB,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,iCAAiC;IACjC,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,8BAA8B;IAC9B,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,gEAAgE;IAChE,cAAc,CAAC,EAAE,MAAM,EAAE,CAAC;CAC1B;AAED;;GAEG;AACH,MAAM,WAAW,sBAAsB;IACtC,sCAAsC;IACtC,gBAAgB,EAAE,MAAM,CAAC;IACzB,oCAAoC;IACpC,cAAc,EAAE,MAAM,CAAC;IACvB,sCAAsC;IACtC,UAAU,EAAE,MAAM,CAAC;IACnB,4CAA4C;IAC5C,SAAS,EAAE,MAAM,CAAC;IAClB,2CAA2C;IAC3C,WAAW,CAAC,EAAE,GAAG,CAAC;IAClB,wCAAwC;IACxC,OAAO,CAAC,EAAE,MAAM,CAAC;CACjB;AAED;;;GAGG;AACH,qBAAa,eAAe;IAC3B;;;;OAIG;WACU,QAAQ,CAAC,OAAO,EAAE,sBAAsB,GAAG,OAAO,CAAC,sBAAsB,CAAC;IAuKvF,OAAO,CAAC,MAAM,CAAC,wBAAwB;IAsBvC,OAAO,CAAC,MAAM,CAAC,4BAA4B;IAsB3C,OAAO,CAAC,MAAM,CAAC,SAAS;IAMxB,OAAO,CAAC,MAAM,CAAC,YAAY;CAY3B"}
@@ -0,0 +1,201 @@
1
+ /**
2
+ * API Documentation Generator
3
+ * Main class for generating OpenAPI and HTML documentation from TypeScript controllers
4
+ */
5
+ import fs from 'node:fs';
6
+ import path from 'node:path';
7
+ import { performance } from 'node:perf_hooks';
8
+ import { ControllerParser } from './controller-parser';
9
+ import { HtmlGenerator } from './html-generator';
10
+ import { OpenApiBuilder } from './openapi-builder';
11
+ import { TypeResolver } from './type-resolver';
12
+ /**
13
+ * API Documentation Generator
14
+ * Generates OpenAPI and HTML documentation from TypeScript controllers
15
+ */
16
+ export class ApiDocGenerator {
17
+ /**
18
+ * Generate API documentation
19
+ * @param options - Generation options with config and paths
20
+ * @returns Generation result with statistics
21
+ */
22
+ static async generate(options) {
23
+ const startTime = performance.now();
24
+ const { config, serverDir, dryRun = false, verbose = false, openApiOnly = false, htmlOnly = false } = options;
25
+ // Initialize components
26
+ const controllerParser = new ControllerParser(serverDir);
27
+ const typeResolver = new TypeResolver(serverDir);
28
+ const openApiBuilder = new OpenApiBuilder();
29
+ const htmlGenerator = new HtmlGenerator();
30
+ // Pass TypeResolver to ControllerParser for service method JSDoc extraction
31
+ controllerParser.setTypeResolver(typeResolver);
32
+ // Pre-load common type definition directories
33
+ const commonTypeDirs = options.commonTypeDirs || [
34
+ path.join(serverDir, 'src', 'data'),
35
+ path.join(serverDir, 'src', 'services', 'types'),
36
+ ];
37
+ for (const dir of commonTypeDirs) {
38
+ if (fs.existsSync(dir)) {
39
+ const files = fs.readdirSync(dir, { recursive: false })
40
+ .filter(f => typeof f === 'string' && f.endsWith('.ts'))
41
+ .map(f => path.join(dir, String(f)));
42
+ for (const file of files) {
43
+ try {
44
+ typeResolver.addSourceFile(file);
45
+ }
46
+ catch {
47
+ // Ignore errors for files that can't be loaded
48
+ }
49
+ }
50
+ }
51
+ }
52
+ // Discover controllers
53
+ const controllers = controllerParser.parseAllControllers('shopApi');
54
+ if (controllers.length === 0) {
55
+ throw new Error('No controllers found');
56
+ }
57
+ // Pre-load all controller source files
58
+ const sourceFileCache = new Map();
59
+ for (const controller of controllers) {
60
+ try {
61
+ const sourceFile = controllerParser.addSourceFileAtPath(controller.filePath);
62
+ sourceFileCache.set(controller.filePath, sourceFile);
63
+ typeResolver.addSourceFile(controller.filePath);
64
+ }
65
+ catch {
66
+ // File might already be loaded or invalid
67
+ }
68
+ }
69
+ // Process endpoints
70
+ const openApiEndpoints = [];
71
+ const htmlEndpoints = [];
72
+ const processedTypes = new Set();
73
+ const typeCache = new Map();
74
+ for (const controller of controllers) {
75
+ const endpointInfo = controllerParser.parseEndpoint(controller.className, 'shopApi');
76
+ const fullPath = endpointInfo?.fullPath || `/api/shop${controller.routePath}`;
77
+ const controllerSourceFile = sourceFileCache.get(controller.filePath);
78
+ for (const method of controller.methods) {
79
+ // Resolve request type schema with caching
80
+ let requestSchema;
81
+ if (method.requestType) {
82
+ const cached = typeCache.get(method.requestType);
83
+ if (cached) {
84
+ requestSchema = cached.schema;
85
+ }
86
+ else {
87
+ const resolvedRequest = typeResolver.resolveType(method.requestType, controllerSourceFile);
88
+ if (resolvedRequest) {
89
+ requestSchema = typeResolver.typeToOpenApiSchema(resolvedRequest);
90
+ processedTypes.add(method.requestType);
91
+ typeCache.set(method.requestType, { resolved: resolvedRequest, schema: requestSchema });
92
+ }
93
+ }
94
+ }
95
+ // Resolve response type schema with caching
96
+ let responseSchema;
97
+ if (method.responseType) {
98
+ const cached = typeCache.get(method.responseType);
99
+ if (cached) {
100
+ responseSchema = cached.schema;
101
+ }
102
+ else {
103
+ const resolvedResponse = typeResolver.resolveType(method.responseType, controllerSourceFile);
104
+ if (resolvedResponse) {
105
+ responseSchema = typeResolver.typeToOpenApiSchema(resolvedResponse);
106
+ processedTypes.add(method.responseType);
107
+ typeCache.set(method.responseType, { resolved: resolvedResponse, schema: responseSchema });
108
+ }
109
+ }
110
+ }
111
+ // Create endpoint data
112
+ const openApiEndpoint = this.controllerToEndpointData(controller, method, fullPath, requestSchema, responseSchema);
113
+ const htmlEndpoint = this.controllerToHtmlEndpointData(controller, method, fullPath, requestSchema, responseSchema);
114
+ openApiEndpoints.push(openApiEndpoint);
115
+ htmlEndpoints.push(htmlEndpoint);
116
+ }
117
+ }
118
+ // Build documentation
119
+ const resolvedTypes = typeResolver.getAllResolvedTypes();
120
+ openApiBuilder.addSchemasFromResolvedTypes(resolvedTypes);
121
+ htmlGenerator.addSchemas(openApiBuilder.getSchemas());
122
+ let openApiSpec;
123
+ let htmlDoc;
124
+ if (!htmlOnly) {
125
+ openApiSpec = openApiBuilder.build(openApiEndpoints);
126
+ }
127
+ if (!openApiOnly) {
128
+ htmlDoc = htmlGenerator.generatePage(htmlEndpoints);
129
+ }
130
+ // Write files if not dry-run
131
+ if (!dryRun) {
132
+ if (openApiSpec) {
133
+ const swaggerDir = config.getOutputPath(serverDir, 'swagger');
134
+ this.ensureDir(swaggerDir);
135
+ fs.writeFileSync(path.join(swaggerDir, config.output.specFilename), JSON.stringify(openApiSpec, null, 2));
136
+ }
137
+ if (htmlDoc) {
138
+ const htmlDir = config.getOutputPath(serverDir, 'html');
139
+ this.ensureDir(htmlDir);
140
+ this.cleanHtmlDir(htmlDir);
141
+ fs.writeFileSync(path.join(htmlDir, 'index.html'), htmlDoc);
142
+ }
143
+ }
144
+ const totalTime = performance.now() - startTime;
145
+ return {
146
+ controllersCount: controllers.length,
147
+ endpointsCount: openApiEndpoints.length,
148
+ typesCount: processedTypes.size,
149
+ totalTime,
150
+ openApiSpec,
151
+ htmlDoc,
152
+ };
153
+ }
154
+ static controllerToEndpointData(controller, method, fullPath, requestSchema, responseSchema) {
155
+ return {
156
+ path: fullPath,
157
+ method: method.httpMethod,
158
+ summary: method.serviceDescription || method.description || undefined,
159
+ description: method.description || undefined,
160
+ tag: controller.tag || 'default',
161
+ auth: method.auth || { hasAuth: false },
162
+ requestSchema,
163
+ responseSchema,
164
+ requestTypeName: method.requestType || undefined,
165
+ responseTypeName: method.responseType || undefined,
166
+ operationId: `${method.name}_${controller.className}`,
167
+ };
168
+ }
169
+ static controllerToHtmlEndpointData(controller, method, fullPath, requestSchema, responseSchema) {
170
+ return {
171
+ path: fullPath,
172
+ method: method.httpMethod,
173
+ summary: method.serviceDescription || method.description || undefined,
174
+ description: method.description || undefined,
175
+ tag: controller.tag || 'default',
176
+ auth: method.auth,
177
+ requestSchema,
178
+ responseSchema,
179
+ requestTypeName: method.requestType || undefined,
180
+ responseTypeName: method.responseType || undefined,
181
+ operationId: `${method.name}_${controller.className}`,
182
+ };
183
+ }
184
+ static ensureDir(dirPath) {
185
+ if (!fs.existsSync(dirPath)) {
186
+ fs.mkdirSync(dirPath, { recursive: true });
187
+ }
188
+ }
189
+ static cleanHtmlDir(dirPath) {
190
+ if (!fs.existsSync(dirPath)) {
191
+ return;
192
+ }
193
+ const files = fs.readdirSync(dirPath);
194
+ for (const file of files) {
195
+ if (file.endsWith('.html')) {
196
+ fs.unlinkSync(path.join(dirPath, file));
197
+ }
198
+ }
199
+ }
200
+ }
201
+ //# sourceMappingURL=api-doc-generator.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"api-doc-generator.js","sourceRoot":"","sources":["../../src/apidoc/api-doc-generator.ts"],"names":[],"mappings":"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"}