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.
- package/LICENSE +21 -0
- package/README.md +302 -0
- package/dist/apidoc/api-doc-generator.d.ts +58 -0
- package/dist/apidoc/api-doc-generator.d.ts.map +1 -0
- package/dist/apidoc/api-doc-generator.js +201 -0
- package/dist/apidoc/api-doc-generator.js.map +1 -0
- package/dist/apidoc/config.d.ts +153 -0
- package/dist/apidoc/config.d.ts.map +1 -0
- package/dist/apidoc/config.js +254 -0
- package/dist/apidoc/config.js.map +1 -0
- package/dist/apidoc/controller-parser.d.ts +208 -0
- package/dist/apidoc/controller-parser.d.ts.map +1 -0
- package/dist/apidoc/controller-parser.js +686 -0
- package/dist/apidoc/controller-parser.js.map +1 -0
- package/dist/apidoc/html-generator.d.ts +290 -0
- package/dist/apidoc/html-generator.d.ts.map +1 -0
- package/dist/apidoc/html-generator.js +2295 -0
- package/dist/apidoc/html-generator.js.map +1 -0
- package/dist/apidoc/index.d.ts +20 -0
- package/dist/apidoc/index.d.ts.map +1 -0
- package/dist/apidoc/index.js +16 -0
- package/dist/apidoc/index.js.map +1 -0
- package/dist/apidoc/openapi-builder.d.ts +169 -0
- package/dist/apidoc/openapi-builder.d.ts.map +1 -0
- package/dist/apidoc/openapi-builder.js +634 -0
- package/dist/apidoc/openapi-builder.js.map +1 -0
- package/dist/apidoc/parameterGeneratorRegistry.d.ts +20 -0
- package/dist/apidoc/parameterGeneratorRegistry.d.ts.map +1 -0
- package/dist/apidoc/parameterGeneratorRegistry.js +6 -0
- package/dist/apidoc/parameterGeneratorRegistry.js.map +1 -0
- package/dist/apidoc/test-type-resolver.d.ts +2 -0
- package/dist/apidoc/test-type-resolver.d.ts.map +1 -0
- package/dist/apidoc/test-type-resolver.js +6 -0
- package/dist/apidoc/test-type-resolver.js.map +1 -0
- package/dist/apidoc/type-resolver.d.ts +266 -0
- package/dist/apidoc/type-resolver.d.ts.map +1 -0
- package/dist/apidoc/type-resolver.js +1226 -0
- package/dist/apidoc/type-resolver.js.map +1 -0
- package/dist/apidoc/verify-type-resolution.d.ts +3 -0
- package/dist/apidoc/verify-type-resolution.d.ts.map +1 -0
- package/dist/apidoc/verify-type-resolution.js +29 -0
- package/dist/apidoc/verify-type-resolution.js.map +1 -0
- package/dist/bun/bunRouter.d.ts +70 -0
- package/dist/bun/bunRouter.d.ts.map +1 -0
- package/dist/bun/bunRouter.js +324 -0
- package/dist/bun/bunRouter.js.map +1 -0
- package/dist/bun/bunServer.d.ts +72 -0
- package/dist/bun/bunServer.d.ts.map +1 -0
- package/dist/bun/bunServer.js +218 -0
- package/dist/bun/bunServer.js.map +1 -0
- package/dist/bun/bunStaticFiles.d.ts +76 -0
- package/dist/bun/bunStaticFiles.d.ts.map +1 -0
- package/dist/bun/bunStaticFiles.js +251 -0
- package/dist/bun/bunStaticFiles.js.map +1 -0
- package/dist/bun/index.d.ts +7 -0
- package/dist/bun/index.d.ts.map +1 -0
- package/dist/bun/index.js +7 -0
- package/dist/bun/index.js.map +1 -0
- package/dist/data-contracts.d.ts +132 -0
- package/dist/data-contracts.d.ts.map +1 -0
- package/dist/data-contracts.js +2 -0
- package/dist/data-contracts.js.map +1 -0
- package/dist/decorators.d.ts +75 -0
- package/dist/decorators.d.ts.map +1 -0
- package/dist/decorators.js +101 -0
- package/dist/decorators.js.map +1 -0
- package/dist/express/expressFrontendRouter.d.ts +17 -0
- package/dist/express/expressFrontendRouter.d.ts.map +1 -0
- package/dist/express/expressFrontendRouter.js +33 -0
- package/dist/express/expressFrontendRouter.js.map +1 -0
- package/dist/express/expressRouter.d.ts +25 -0
- package/dist/express/expressRouter.d.ts.map +1 -0
- package/dist/express/expressRouter.js +150 -0
- package/dist/express/expressRouter.js.map +1 -0
- package/dist/express/index.d.ts +6 -0
- package/dist/express/index.d.ts.map +1 -0
- package/dist/express/index.js +6 -0
- package/dist/express/index.js.map +1 -0
- package/dist/index.d.ts +47 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +52 -0
- package/dist/index.js.map +1 -0
- package/dist/router.d.ts +162 -0
- package/dist/router.d.ts.map +1 -0
- package/dist/router.js +350 -0
- package/dist/router.js.map +1 -0
- package/dist/runtime-detect.d.ts +20 -0
- package/dist/runtime-detect.d.ts.map +1 -0
- package/dist/runtime-detect.js +20 -0
- package/dist/runtime-detect.js.map +1 -0
- package/dist/server.d.ts +126 -0
- package/dist/server.d.ts.map +1 -0
- package/dist/server.js +181 -0
- package/dist/server.js.map +1 -0
- package/dist/utils.d.ts +83 -0
- package/dist/utils.d.ts.map +1 -0
- package/dist/utils.js +157 -0
- package/dist/utils.js.map +1 -0
- 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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"}
|