@mena-emad/documind 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/README.md +221 -0
- package/dist/cli/index.d.ts +15 -0
- package/dist/cli/index.d.ts.map +1 -0
- package/dist/cli/index.js +22 -0
- package/dist/cli/index.js.map +1 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +118 -0
- package/dist/index.js.map +1 -0
- package/dist/parser/fileScanner.d.ts +7 -0
- package/dist/parser/fileScanner.d.ts.map +1 -0
- package/dist/parser/fileScanner.js +29 -0
- package/dist/parser/fileScanner.js.map +1 -0
- package/dist/parser/routerExtractor.d.ts +7 -0
- package/dist/parser/routerExtractor.d.ts.map +1 -0
- package/dist/parser/routerExtractor.js +27 -0
- package/dist/parser/routerExtractor.js.map +1 -0
- package/dist/services/aiService.d.ts +14 -0
- package/dist/services/aiService.d.ts.map +1 -0
- package/dist/services/aiService.js +75 -0
- package/dist/services/aiService.js.map +1 -0
- package/dist/utils/fileWriter.d.ts +14 -0
- package/dist/utils/fileWriter.d.ts.map +1 -0
- package/dist/utils/fileWriter.js +48 -0
- package/dist/utils/fileWriter.js.map +1 -0
- package/package.json +54 -0
package/README.md
ADDED
|
@@ -0,0 +1,221 @@
|
|
|
1
|
+
# DocuMind
|
|
2
|
+
|
|
3
|
+
[](https://github.com/mena-emad/DocuMind) [](./LICENSE) [](https://www.typescriptlang.org/)
|
|
4
|
+
|
|
5
|
+
> Generate production-ready Swagger/OpenAPI docs from Express.js code using Gemini AI and smart context stitching.
|
|
6
|
+
|
|
7
|
+
## š Why DocuMind?
|
|
8
|
+
|
|
9
|
+
DocuMind is designed to fix the common problem of shallow or empty generated API docs.
|
|
10
|
+
Instead of only parsing route definitions, it intelligently collects surrounding business logic so Gemini understands each endpoint in context.
|
|
11
|
+
|
|
12
|
+
- Extracts routes, controllers, services, models, and validations
|
|
13
|
+
- Encourages deep summaries and descriptions from Gemini
|
|
14
|
+
- Prevents empty `paths` and weak documentation
|
|
15
|
+
- Keeps generated output aligned with your implementation
|
|
16
|
+
|
|
17
|
+
## šÆ Showcase
|
|
18
|
+
|
|
19
|
+
### Input
|
|
20
|
+
A simple Express route plus related service and validation code.
|
|
21
|
+
|
|
22
|
+
```ts
|
|
23
|
+
// src/routes/auth.ts
|
|
24
|
+
router.post('/login', authController.login);
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
### Output
|
|
28
|
+
|
|
29
|
+
```yaml
|
|
30
|
+
openapi: 3.0.0
|
|
31
|
+
info:
|
|
32
|
+
title: DocuMind Generated API
|
|
33
|
+
version: 1.0.0
|
|
34
|
+
description: Automated OpenAPI documentation generated by DocuMind AI.
|
|
35
|
+
paths:
|
|
36
|
+
/login:
|
|
37
|
+
post:
|
|
38
|
+
tags:
|
|
39
|
+
- Auth
|
|
40
|
+
summary: Authenticate user and issue a JWT token.
|
|
41
|
+
description: |
|
|
42
|
+
Verifies credentials against the hashed user password, checks authorization
|
|
43
|
+
roles, signs a JWT token, and returns a session payload.
|
|
44
|
+
requestBody:
|
|
45
|
+
required: true
|
|
46
|
+
content:
|
|
47
|
+
application/json:
|
|
48
|
+
schema:
|
|
49
|
+
$ref: '#/components/schemas/LoginRequest'
|
|
50
|
+
responses:
|
|
51
|
+
'200':
|
|
52
|
+
description: User authenticated successfully.
|
|
53
|
+
components:
|
|
54
|
+
schemas:
|
|
55
|
+
LoginRequest:
|
|
56
|
+
type: object
|
|
57
|
+
properties:
|
|
58
|
+
email:
|
|
59
|
+
type: string
|
|
60
|
+
format: email
|
|
61
|
+
password:
|
|
62
|
+
type: string
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
## š§ How it Works
|
|
66
|
+
|
|
67
|
+
### Context Stitching
|
|
68
|
+
DocuMind attaches nearby compute context to route files so Gemini sees the full backend flow.
|
|
69
|
+
|
|
70
|
+
```mermaid
|
|
71
|
+
flowchart LR
|
|
72
|
+
A[Route File] --> B[Controller Code]
|
|
73
|
+
A --> C[Service Logic]
|
|
74
|
+
A --> D[Validation / Models]
|
|
75
|
+
B --> E[AI Prompt]
|
|
76
|
+
C --> E
|
|
77
|
+
D --> E
|
|
78
|
+
E --> F[Gemini AI]
|
|
79
|
+
F --> G[OpenAPI Output]
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
### Generation Flow
|
|
83
|
+
|
|
84
|
+
```mermaid
|
|
85
|
+
flowchart TD
|
|
86
|
+
A[CLI Start] --> B[Parse CLI Options]
|
|
87
|
+
B --> C[Scan Project Directory]
|
|
88
|
+
C --> D[Filter Express Route Files]
|
|
89
|
+
D --> E[Collect Related Context]
|
|
90
|
+
E --> F[Call Gemini Model]
|
|
91
|
+
F --> G[Defensive Merge Output]
|
|
92
|
+
G --> H[Write swagger.yaml / json]
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
## ⨠Key Features
|
|
96
|
+
|
|
97
|
+
- **Smart File Scanning**: finds Express route files across the project.
|
|
98
|
+
- **Context Stitching**: includes controllers, services, models, and validation code.
|
|
99
|
+
- **Defensive Merging**: merges AI-generated `paths` and `schemas` safely.
|
|
100
|
+
- **Rich Descriptions**: forces Gemini to explain workflows like hashing, JWT creation, roles, and errors.
|
|
101
|
+
- **Interactive CLI**: animated progress using `ora`.
|
|
102
|
+
|
|
103
|
+
## š§° Tech Stack
|
|
104
|
+
|
|
105
|
+
- Node.js (>= 18.0.0)
|
|
106
|
+
- TypeScript
|
|
107
|
+
- Google Gen AI SDK (`@google/genai`)
|
|
108
|
+
- Commander.js
|
|
109
|
+
- Ora
|
|
110
|
+
- Dotenv
|
|
111
|
+
- yaml
|
|
112
|
+
|
|
113
|
+
## š„ Installation
|
|
114
|
+
|
|
115
|
+
### Global
|
|
116
|
+
|
|
117
|
+
```bash
|
|
118
|
+
npm install -g @mena-emad/documind
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
### Local
|
|
122
|
+
|
|
123
|
+
```bash
|
|
124
|
+
git clone https://github.com/mena-emad/DocuMind.git
|
|
125
|
+
cd DocuMind
|
|
126
|
+
npm install
|
|
127
|
+
npm run build
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
Run in development mode:
|
|
131
|
+
|
|
132
|
+
```bash
|
|
133
|
+
npm run dev
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
Run locally:
|
|
137
|
+
|
|
138
|
+
```bash
|
|
139
|
+
npx @mena-emad/documind
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
## š§ Environment Variables
|
|
143
|
+
|
|
144
|
+
DocuMind reads the Gemini API key from `.env`:
|
|
145
|
+
|
|
146
|
+
```env
|
|
147
|
+
DOCUMIND_API_KEY=your_gemini_api_key_here
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
Or pass the key directly using `-k`.
|
|
151
|
+
|
|
152
|
+
## ā¶ļø Usage
|
|
153
|
+
|
|
154
|
+
```bash
|
|
155
|
+
documind [options]
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
### CLI Options
|
|
159
|
+
|
|
160
|
+
| Option | Description | Default |
|
|
161
|
+
|---|---|---|
|
|
162
|
+
| `-k, --key <api-key>` | Gemini API Key | `DOCUMIND_API_KEY` from `.env` |
|
|
163
|
+
| `-d, --dir [directory]` | Directory to scan | `./` |
|
|
164
|
+
| `-o, --output [file-name]` | Output filename | `swagger.yaml` |
|
|
165
|
+
| `--title [title]` | OpenAPI `info.title` | auto-generated |
|
|
166
|
+
| `--api-version [version]` | OpenAPI `info.version` | auto-generated |
|
|
167
|
+
| `--desc [description]` | OpenAPI `info.description` | auto-generated |
|
|
168
|
+
| `-v, --version` | Show CLI version | - |
|
|
169
|
+
|
|
170
|
+
### Examples
|
|
171
|
+
|
|
172
|
+
Generate default output:
|
|
173
|
+
|
|
174
|
+
```bash
|
|
175
|
+
documind
|
|
176
|
+
```
|
|
177
|
+
|
|
178
|
+
Generate docs for a custom source directory:
|
|
179
|
+
|
|
180
|
+
```bash
|
|
181
|
+
documind -d ./src -o api-docs.yaml
|
|
182
|
+
```
|
|
183
|
+
|
|
184
|
+
Use the API key directly:
|
|
185
|
+
|
|
186
|
+
```bash
|
|
187
|
+
documind -k sk-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
|
|
188
|
+
```
|
|
189
|
+
|
|
190
|
+
Provide OpenAPI metadata:
|
|
191
|
+
|
|
192
|
+
```bash
|
|
193
|
+
documind --title "My App API" --api-version "2.0.0" --desc "AI-generated Swagger docs from Express source code."
|
|
194
|
+
```
|
|
195
|
+
|
|
196
|
+
## š How DocuMind Processes Code
|
|
197
|
+
|
|
198
|
+
1. **Scan files**
|
|
199
|
+
- Recursively collect `.js` and `.ts` files.
|
|
200
|
+
2. **Filter Express routes**
|
|
201
|
+
- Detect route patterns and Express router usage.
|
|
202
|
+
3. **Gather related context**
|
|
203
|
+
- Add matching service, controller, model, and validation files.
|
|
204
|
+
4. **Ask Gemini**
|
|
205
|
+
- Send a prompt that demands valid OpenAPI JSON and rich documentation.
|
|
206
|
+
5. **Merge outputs defensively**
|
|
207
|
+
- Combine returned `paths` and `components` while handling missing or malformed sections.
|
|
208
|
+
6. **Write output**
|
|
209
|
+
- Save YAML or JSON based on the requested filename.
|
|
210
|
+
|
|
211
|
+
## šØāš» About the Author
|
|
212
|
+
|
|
213
|
+
Built by **Mena Emad Sawares**.
|
|
214
|
+
|
|
215
|
+
- Website: [https://menaemad.vercel.app/](https://menaemad.vercel.app/)
|
|
216
|
+
- GitHub: [https://github.com/mena-emad](https://github.com/mena-emad)
|
|
217
|
+
- Email: [menaemad6543@gmail.com](mailto:menaemad6543@gmail.com)
|
|
218
|
+
|
|
219
|
+
## š License
|
|
220
|
+
|
|
221
|
+
DocuMind is released under the **ISC License**.
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
interface CLIOptions {
|
|
2
|
+
key?: string;
|
|
3
|
+
dir: string;
|
|
4
|
+
output: string;
|
|
5
|
+
title?: string;
|
|
6
|
+
description?: string;
|
|
7
|
+
version?: string;
|
|
8
|
+
}
|
|
9
|
+
/**
|
|
10
|
+
* Parses and returns command-line options for DocuMind.
|
|
11
|
+
* @returns Parsed CLI options.
|
|
12
|
+
*/
|
|
13
|
+
export default function parseCLI(): CLIOptions;
|
|
14
|
+
export {};
|
|
15
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/cli/index.ts"],"names":[],"mappings":"AAEA,UAAU,UAAU;IAChB,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,GAAG,EAAE,MAAM,CAAC;IACZ,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,OAAO,CAAC,EAAE,MAAM,CAAC;CACpB;AAED;;;GAGG;AACH,MAAM,CAAC,OAAO,UAAU,QAAQ,IAAI,UAAU,CAkB7C"}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { Command } from 'commander';
|
|
2
|
+
/**
|
|
3
|
+
* Parses and returns command-line options for DocuMind.
|
|
4
|
+
* @returns Parsed CLI options.
|
|
5
|
+
*/
|
|
6
|
+
export default function parseCLI() {
|
|
7
|
+
const program = new Command();
|
|
8
|
+
program
|
|
9
|
+
.name("documind")
|
|
10
|
+
.description("AI-Powered Swagger/OpenAPI Documentation Generator for Express.js")
|
|
11
|
+
.version("1.0.0", "-v, --version", "--version for version of documind CLI");
|
|
12
|
+
program
|
|
13
|
+
.option('-d, --dir [directory]', 'The directory to scan for Express routes', './')
|
|
14
|
+
.option('-k, --key <api-key>', 'Your Gemini API Key (Alternative to .env)')
|
|
15
|
+
.option('-o, --output [file-name]', 'The output file name', 'swagger.yaml')
|
|
16
|
+
.option('--title [title]', 'API Documentation Title')
|
|
17
|
+
.option('--api-version [version]', 'API Document Version')
|
|
18
|
+
.option('--desc [description]', 'API Description');
|
|
19
|
+
program.parse(process.argv);
|
|
20
|
+
return program.opts();
|
|
21
|
+
}
|
|
22
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/cli/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAWpC;;;GAGG;AACH,MAAM,CAAC,OAAO,UAAU,QAAQ;IAC5B,MAAM,OAAO,GAAY,IAAI,OAAO,EAAE,CAAC;IAEvC,OAAO;SACN,IAAI,CAAC,UAAU,CAAC;SAChB,WAAW,CAAC,mEAAmE,CAAC;SAChF,OAAO,CAAC,OAAO,EAAC,eAAe,EAAG,uCAAuC,CAAC,CAAC;IAE5E,OAAO;SACN,MAAM,CAAC,uBAAuB,EAAE,0CAA0C,EAAE,IAAI,CAAC;SACjF,MAAM,CAAC,qBAAqB,EAAE,2CAA2C,CAAC;SAC1E,MAAM,CAAC,0BAA0B,EAAE,sBAAsB,EAAE,cAAc,CAAC;SAC1E,MAAM,CAAC,iBAAiB,EAAE,yBAAyB,CAAC;SACpD,MAAM,CAAC,yBAAyB,EAAE,sBAAsB,CAAC;SACzD,MAAM,CAAC,sBAAsB,EAAE,iBAAiB,CAAC,CAAC;IACnD,OAAO,CAAC,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;IAE5B,OAAO,OAAO,CAAC,IAAI,EAAc,CAAC;AACtC,CAAC"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":""}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import parseCLI from './cli/index.js';
|
|
3
|
+
import { AIService } from './services/aiService.js';
|
|
4
|
+
import scanDirectory from './parser/fileScanner.js';
|
|
5
|
+
import isExpressRouteFile from './parser/routerExtractor.js';
|
|
6
|
+
import { writeSwaggerFile } from './utils/fileWriter.js';
|
|
7
|
+
import * as fs from 'fs';
|
|
8
|
+
import * as path from 'path';
|
|
9
|
+
import ora from 'ora';
|
|
10
|
+
/**
|
|
11
|
+
* Entry point for the DocuMind CLI.
|
|
12
|
+
* It scans the project, sends merged route context to Gemini, merges responses, and writes the output file.
|
|
13
|
+
*/
|
|
14
|
+
async function main() {
|
|
15
|
+
console.log("š§ Welcome to DocuMind! Let's build some magic...\n");
|
|
16
|
+
const options = parseCLI();
|
|
17
|
+
let aiService;
|
|
18
|
+
try {
|
|
19
|
+
aiService = new AIService(options.key);
|
|
20
|
+
}
|
|
21
|
+
catch (err) {
|
|
22
|
+
console.error(err.message);
|
|
23
|
+
process.exit(1);
|
|
24
|
+
}
|
|
25
|
+
let dirInput = './';
|
|
26
|
+
if (typeof options.dir === 'string') {
|
|
27
|
+
dirInput = options.dir;
|
|
28
|
+
}
|
|
29
|
+
const targetDir = path.resolve(dirInput);
|
|
30
|
+
const outputFileName = typeof options.output === 'string' ? options.output : 'swagger.yaml';
|
|
31
|
+
const scanSpinner = ora(`Scanning directory: ${targetDir}`).start();
|
|
32
|
+
const allFiles = scanDirectory(targetDir);
|
|
33
|
+
const validRouteFiles = allFiles.filter(file => isExpressRouteFile(file));
|
|
34
|
+
scanSpinner.succeed(`š Found ${allFiles.length} total files. | šÆ ${validRouteFiles.length} Express controller/route files detected.\n`);
|
|
35
|
+
if (validRouteFiles.length === 0) {
|
|
36
|
+
console.log("ā ļø No valid Express routes or controllers detected to document.");
|
|
37
|
+
return;
|
|
38
|
+
}
|
|
39
|
+
const finalPaths = {};
|
|
40
|
+
const finalComponents = {};
|
|
41
|
+
for (const file of validRouteFiles) {
|
|
42
|
+
const fileName = path.basename(file);
|
|
43
|
+
if (fileName === 'app.js' || fileName === 'index.js' || fileName === 'server.js') {
|
|
44
|
+
continue;
|
|
45
|
+
}
|
|
46
|
+
const fileSpinner = ora(`Analyzing file: ${fileName}...`).start();
|
|
47
|
+
const fileContent = fs.readFileSync(file, 'utf8');
|
|
48
|
+
const prefix = fileName.split('.')[0] || '';
|
|
49
|
+
let additionalContext = '';
|
|
50
|
+
const relatedFiles = allFiles.filter(f => {
|
|
51
|
+
const name = path.basename(f).toLowerCase();
|
|
52
|
+
return name.startsWith(prefix.toLowerCase()) && f !== file;
|
|
53
|
+
});
|
|
54
|
+
for (const related of relatedFiles) {
|
|
55
|
+
const relName = path.basename(related);
|
|
56
|
+
if (relName.includes('service') || relName.includes('controller') || relName.includes('validation') || relName.includes('model')) {
|
|
57
|
+
fileSpinner.text = `š Injecting context into ${fileName} from: ${relName}`;
|
|
58
|
+
additionalContext += `\n\n[Attached Related Context - ${relName}]:\n${fs.readFileSync(related, 'utf8')}`;
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
const totalPayload = fileContent + additionalContext;
|
|
62
|
+
fileSpinner.text = `ā³ Waiting for Gemini to generate docs for ${fileName}...`;
|
|
63
|
+
try {
|
|
64
|
+
const docResult = await aiService.generateDoc(totalPayload);
|
|
65
|
+
fileSpinner.stop();
|
|
66
|
+
fileSpinner.start(`Processing and merging JSON data for ${fileName}...`);
|
|
67
|
+
const aiJson = JSON.parse(docResult);
|
|
68
|
+
if (aiJson.paths && Object.keys(aiJson.paths).length > 0) {
|
|
69
|
+
Object.assign(finalPaths, aiJson.paths);
|
|
70
|
+
}
|
|
71
|
+
else {
|
|
72
|
+
const upperPaths = aiJson.Paths || aiJson.PATHS;
|
|
73
|
+
if (upperPaths)
|
|
74
|
+
Object.assign(finalPaths, upperPaths);
|
|
75
|
+
}
|
|
76
|
+
if (aiJson.components) {
|
|
77
|
+
if (aiJson.components.schemas) {
|
|
78
|
+
finalComponents.schemas = {
|
|
79
|
+
...finalComponents.schemas,
|
|
80
|
+
...aiJson.components.schemas
|
|
81
|
+
};
|
|
82
|
+
}
|
|
83
|
+
for (const key of Object.keys(aiJson.components)) {
|
|
84
|
+
if (key !== 'schemas') {
|
|
85
|
+
finalComponents[key] = {
|
|
86
|
+
...finalComponents[key],
|
|
87
|
+
...aiJson.components[key]
|
|
88
|
+
};
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
fileSpinner.succeed(`ā
${fileName} parsed and merged successfully.`);
|
|
93
|
+
}
|
|
94
|
+
catch (err) {
|
|
95
|
+
fileSpinner.fail(`ā Failed to parse AI response for ${fileName}. Raw response structure invalid.`);
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
const totalData = {
|
|
99
|
+
openapi: '3.0.0',
|
|
100
|
+
paths: finalPaths,
|
|
101
|
+
...(Object.keys(finalComponents).length > 0 ? { components: finalComponents } : {})
|
|
102
|
+
};
|
|
103
|
+
console.log('\nš Final Data Object Structure Ready.');
|
|
104
|
+
const writeSpinner = ora('Writing final Swagger documentation...').start();
|
|
105
|
+
try {
|
|
106
|
+
writeSwaggerFile(outputFileName, totalData, {
|
|
107
|
+
title: typeof options.title === 'string' ? options.title : undefined,
|
|
108
|
+
version: typeof options.apiVersion === 'string' ? options.apiVersion : undefined,
|
|
109
|
+
desc: typeof options.desc === 'string' ? options.desc : undefined
|
|
110
|
+
});
|
|
111
|
+
writeSpinner.succeed(`š Success! Swagger documentation saved to: ${outputFileName}`);
|
|
112
|
+
}
|
|
113
|
+
catch (writeError) {
|
|
114
|
+
writeSpinner.fail('ā Failed to write the documentation file.');
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
main();
|
|
118
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"fileScanner.d.ts","sourceRoot":"","sources":["../../src/parser/fileScanner.ts"],"names":[],"mappings":"AAGA;;;;GAIG;AACH,MAAM,CAAC,OAAO,UAAU,aAAa,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,EAAE,CAuB3D"}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import * as fs from 'fs';
|
|
2
|
+
import * as path from 'path';
|
|
3
|
+
/**
|
|
4
|
+
* Recursively scans a directory for JavaScript and TypeScript files.
|
|
5
|
+
* @param dir Directory to scan.
|
|
6
|
+
* @returns Array of matched file paths.
|
|
7
|
+
*/
|
|
8
|
+
export default function scanDirectory(dir) {
|
|
9
|
+
let results = [];
|
|
10
|
+
const list = fs.readdirSync(dir);
|
|
11
|
+
list.forEach((file) => {
|
|
12
|
+
const fullPath = path.join(dir, file);
|
|
13
|
+
const stat = fs.statSync(fullPath);
|
|
14
|
+
if (file === 'node_modules' || file.startsWith('.')) {
|
|
15
|
+
return;
|
|
16
|
+
}
|
|
17
|
+
if (stat && stat.isDirectory()) {
|
|
18
|
+
results = results.concat(scanDirectory(fullPath));
|
|
19
|
+
}
|
|
20
|
+
else {
|
|
21
|
+
const ext = path.extname(fullPath);
|
|
22
|
+
if (['.js', '.ts'].includes(ext)) {
|
|
23
|
+
results.push(fullPath);
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
});
|
|
27
|
+
return results;
|
|
28
|
+
}
|
|
29
|
+
//# sourceMappingURL=fileScanner.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"fileScanner.js","sourceRoot":"","sources":["../../src/parser/fileScanner.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,IAAI,CAAC;AACzB,OAAO,KAAK,IAAI,MAAM,MAAM,CAAC;AAE7B;;;;GAIG;AACH,MAAM,CAAC,OAAO,UAAU,aAAa,CAAC,GAAW;IAC7C,IAAI,OAAO,GAAa,EAAE,CAAC;IAC3B,MAAM,IAAI,GAAG,EAAE,CAAC,WAAW,CAAC,GAAG,CAAC,CAAC;IAEjC,IAAI,CAAC,OAAO,CAAC,CAAC,IAAI,EAAE,EAAE;QAClB,MAAM,QAAQ,GAAG,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,IAAI,CAAC,CAAC;QACtC,MAAM,IAAI,GAAG,EAAE,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAC;QAEnC,IAAI,IAAI,KAAK,cAAc,IAAI,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,EAAE,CAAC;YAClD,OAAO;QACX,CAAC;QAED,IAAI,IAAI,IAAI,IAAI,CAAC,WAAW,EAAE,EAAE,CAAC;YAC7B,OAAO,GAAG,OAAO,CAAC,MAAM,CAAC,aAAa,CAAC,QAAQ,CAAC,CAAC,CAAC;QACtD,CAAC;aAAM,CAAC;YACJ,MAAM,GAAG,GAAG,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC;YACnC,IAAI,CAAC,KAAK,EAAE,KAAK,CAAC,CAAC,QAAQ,CAAC,GAAG,CAAC,EAAE,CAAC;gBAC/B,OAAO,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;YAC3B,CAAC;QACL,CAAC;IACL,CAAC,CAAC,CAAC;IAEH,OAAO,OAAO,CAAC;AACnB,CAAC"}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ŲÆŲ§ŁŲ© Ų°ŁŁŲ© ŲŖŁŲŲµ Ł
ŲŲŖŁŁ Ų§ŁŁ
ŁŁ ŁŲŖŲŲÆŲÆ ŁŁ ŁŁ Ł
ŁŁ Express Controller/Router ŲŁŁŁŁ ŁŲ³ŲŖŲŁ Ų§ŁŁ Documentation Ų£Ł
ŁŲ§
|
|
3
|
+
* @param filePath Ų§ŁŁ
Ų³Ų§Ų± Ų§ŁŁŲ§Ł
Ł ŁŁŁ
ŁŁ Ų§ŁŁ
Ų±Ų§ŲÆ ŁŲŲµŁ
|
|
4
|
+
* @returns boolean (true ŁŁ Ų§ŁŁ
ŁŁ ŁŲŲŖŁŁ Ų¹ŁŁ ŁŁŲÆ Ų„ŁŲ³ŲØŲ±ŁŲ³ ŲŁŁŁŁ)
|
|
5
|
+
*/
|
|
6
|
+
export default function isExpressRouteFile(filePath: string): boolean;
|
|
7
|
+
//# sourceMappingURL=routerExtractor.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"routerExtractor.d.ts","sourceRoot":"","sources":["../../src/parser/routerExtractor.ts"],"names":[],"mappings":"AAEA;;;;GAIG;AACH,MAAM,CAAC,OAAO,UAAU,kBAAkB,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAsBpE"}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import * as fs from 'fs';
|
|
2
|
+
/**
|
|
3
|
+
* ŲÆŲ§ŁŲ© Ų°ŁŁŲ© ŲŖŁŲŲµ Ł
ŲŲŖŁŁ Ų§ŁŁ
ŁŁ ŁŲŖŲŲÆŲÆ ŁŁ ŁŁ Ł
ŁŁ Express Controller/Router ŲŁŁŁŁ ŁŲ³ŲŖŲŁ Ų§ŁŁ Documentation Ų£Ł
ŁŲ§
|
|
4
|
+
* @param filePath Ų§ŁŁ
Ų³Ų§Ų± Ų§ŁŁŲ§Ł
Ł ŁŁŁ
ŁŁ Ų§ŁŁ
Ų±Ų§ŲÆ ŁŲŲµŁ
|
|
5
|
+
* @returns boolean (true ŁŁ Ų§ŁŁ
ŁŁ ŁŲŲŖŁŁ Ų¹ŁŁ ŁŁŲÆ Ų„ŁŲ³ŲØŲ±ŁŲ³ ŲŁŁŁŁ)
|
|
6
|
+
*/
|
|
7
|
+
export default function isExpressRouteFile(filePath) {
|
|
8
|
+
if (filePath.endsWith('.d.ts') ||
|
|
9
|
+
filePath.includes('.test.') ||
|
|
10
|
+
filePath.includes('.spec.') ||
|
|
11
|
+
filePath.endsWith('tsconfig.json') ||
|
|
12
|
+
filePath.endsWith('package.json')) {
|
|
13
|
+
return false;
|
|
14
|
+
}
|
|
15
|
+
try {
|
|
16
|
+
const content = fs.readFileSync(filePath, 'utf8');
|
|
17
|
+
const hasExpress = content.includes('express');
|
|
18
|
+
const hasRouter = content.includes('Router') || content.includes('router.');
|
|
19
|
+
const hasHttpMethods = /router\.(get|post|put|delete|patch)/i.test(content) || /app\.(get|post|put|delete|patch)/i.test(content);
|
|
20
|
+
const hasReqRes = content.includes('req,res') && content.includes('request,response');
|
|
21
|
+
return (hasExpress && hasRouter) || hasHttpMethods || hasReqRes;
|
|
22
|
+
}
|
|
23
|
+
catch (error) {
|
|
24
|
+
return false;
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
//# sourceMappingURL=routerExtractor.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"routerExtractor.js","sourceRoot":"","sources":["../../src/parser/routerExtractor.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,IAAI,CAAC;AAEzB;;;;GAIG;AACH,MAAM,CAAC,OAAO,UAAU,kBAAkB,CAAC,QAAgB;IACvD,IACI,QAAQ,CAAC,QAAQ,CAAC,OAAO,CAAC;QAC1B,QAAQ,CAAC,QAAQ,CAAC,QAAQ,CAAC;QAC3B,QAAQ,CAAC,QAAQ,CAAC,QAAQ,CAAC;QAC3B,QAAQ,CAAC,QAAQ,CAAC,eAAe,CAAC;QAClC,QAAQ,CAAC,QAAQ,CAAC,cAAc,CAAC,EACnC,CAAC;QACC,OAAO,KAAK,CAAC;IACjB,CAAC;IAED,IAAI,CAAC;QACD,MAAM,OAAO,GAAG,EAAE,CAAC,YAAY,CAAC,QAAQ,EAAE,MAAM,CAAC,CAAC;QAClD,MAAM,UAAU,GAAG,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAC,CAAC;QAC/C,MAAM,SAAS,GAAG,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAC,IAAI,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAC,CAAC;QAC5E,MAAM,cAAc,GAAG,sCAAsC,CAAC,IAAI,CAAC,OAAO,CAAC,IAAI,mCAAmC,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;QACjI,MAAM,SAAS,GAAG,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAC,IAAI,OAAO,CAAC,QAAQ,CAAC,kBAAkB,CAAC,CAAC;QAEtF,OAAO,CAAC,UAAU,IAAI,SAAS,CAAC,IAAI,cAAc,IAAI,SAAS,CAAC;IACpE,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACb,OAAO,KAAK,CAAC;IACjB,CAAC;AACL,CAAC"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* AIService wraps the Google Gemini client to generate OpenAPI documentation.
|
|
3
|
+
*/
|
|
4
|
+
export declare class AIService {
|
|
5
|
+
private ai;
|
|
6
|
+
constructor(apiKey?: string);
|
|
7
|
+
/**
|
|
8
|
+
* Generates documentation output for the provided source code context.
|
|
9
|
+
* @param fileContent Combined route file content and attached context.
|
|
10
|
+
* @returns A JSON string representing the generated OpenAPI payload.
|
|
11
|
+
*/
|
|
12
|
+
generateDoc(fileContent: string): Promise<string>;
|
|
13
|
+
}
|
|
14
|
+
//# sourceMappingURL=aiService.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"aiService.d.ts","sourceRoot":"","sources":["../../src/services/aiService.ts"],"names":[],"mappings":"AAMA;;GAEG;AACH,qBAAa,SAAS;IACpB,OAAO,CAAC,EAAE,CAAc;gBAEZ,MAAM,CAAC,EAAE,MAAM;IAQ3B;;;;OAIG;IACG,WAAW,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;CAsDxD"}
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import { GoogleGenAI } from '@google/genai';
|
|
2
|
+
import * as dotenv from 'dotenv';
|
|
3
|
+
import * as path from 'path';
|
|
4
|
+
dotenv.config({ path: path.resolve(process.cwd(), '.env') });
|
|
5
|
+
/**
|
|
6
|
+
* AIService wraps the Google Gemini client to generate OpenAPI documentation.
|
|
7
|
+
*/
|
|
8
|
+
export class AIService {
|
|
9
|
+
ai;
|
|
10
|
+
constructor(apiKey) {
|
|
11
|
+
const key = apiKey || process.env.DOCUMIND_API_KEY;
|
|
12
|
+
if (!key) {
|
|
13
|
+
throw new Error('ā Gemini API Key is missing! Please provide it via -k or set DOCUMIND_API_KEY environment variable.');
|
|
14
|
+
}
|
|
15
|
+
this.ai = new GoogleGenAI({ apiKey: key });
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Generates documentation output for the provided source code context.
|
|
19
|
+
* @param fileContent Combined route file content and attached context.
|
|
20
|
+
* @returns A JSON string representing the generated OpenAPI payload.
|
|
21
|
+
*/
|
|
22
|
+
async generateDoc(fileContent) {
|
|
23
|
+
const prompt = `
|
|
24
|
+
You are an expert Senior Backend Engineer and a world-class OpenAPI/Swagger Architect.
|
|
25
|
+
Analyze the following Express.js source code (including its routes, controllers, and validations).
|
|
26
|
+
|
|
27
|
+
Your absolute main task is to extract all API endpoints, methods, and schemas to populate a valid OpenAPI 3.0 JSON object.
|
|
28
|
+
|
|
29
|
+
Strict Instructions for High-Quality Documentation:
|
|
30
|
+
1. **DO NOT return an empty paths object**. You must extract the routes found in the code (e.g., /register, /login, /logout, /profile).
|
|
31
|
+
2. **RICH DESCRIPTIONS AND SUMMARIES (CRITICAL)**: For every single operation (get, post, put, delete, etc.) under each route, you MUST include:
|
|
32
|
+
- "summary": A short, clear, one-sentence explanation of what the endpoint does.
|
|
33
|
+
- "description": A highly detailed paragraph explaining the inner mechanics and business logic of the endpoint. For example, explain if it performs password hashing, check if user exists, generates a JWT session token, or requires specific middleware/roles. Do not make it generic.
|
|
34
|
+
3. For each route, trace its controller function to document the expected request body, parameters, and HTTP response status codes (like 200, 201, 400, 401, 500) with descriptive answers for each status.
|
|
35
|
+
4. Extract payload fields into components.schemas.
|
|
36
|
+
5. **CRITICAL REGEX HANDLING**: If you write any regular expressions inside string patterns, double-escape all backslashes (use \\\\ instead of \\) so the output is perfectly valid JSON.
|
|
37
|
+
6. Return ONLY a valid JSON object matching this schema style:
|
|
38
|
+
{
|
|
39
|
+
"paths": {
|
|
40
|
+
"/api/route": {
|
|
41
|
+
"post": {
|
|
42
|
+
"tags": ["Auth"],
|
|
43
|
+
"summary": "...",
|
|
44
|
+
"description": "...",
|
|
45
|
+
"requestBody": { ... },
|
|
46
|
+
"responses": { ... }
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
},
|
|
50
|
+
"components": {
|
|
51
|
+
"schemas": { ... }
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
`;
|
|
55
|
+
try {
|
|
56
|
+
const response = await this.ai.models.generateContent({
|
|
57
|
+
model: 'gemini-2.5-flash',
|
|
58
|
+
contents: [prompt, fileContent],
|
|
59
|
+
config: {
|
|
60
|
+
responseMimeType: 'application/json'
|
|
61
|
+
}
|
|
62
|
+
});
|
|
63
|
+
let cleanText = response.text || '{}';
|
|
64
|
+
if (cleanText.includes('```')) {
|
|
65
|
+
cleanText = cleanText.replace(/```json/g, '').replace(/```/g, '').trim();
|
|
66
|
+
}
|
|
67
|
+
return cleanText;
|
|
68
|
+
}
|
|
69
|
+
catch (apiError) {
|
|
70
|
+
console.error(`\nā Gemini API Error: ${apiError.message}`);
|
|
71
|
+
return '{}';
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
//# sourceMappingURL=aiService.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"aiService.js","sourceRoot":"","sources":["../../src/services/aiService.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AAC5C,OAAO,KAAK,MAAM,MAAM,QAAQ,CAAC;AACjC,OAAO,KAAK,IAAI,MAAM,MAAM,CAAC;AAE7B,MAAM,CAAC,MAAM,CAAC,EAAE,IAAI,EAAE,IAAI,CAAC,OAAO,CAAC,OAAO,CAAC,GAAG,EAAE,EAAE,MAAM,CAAC,EAAE,CAAC,CAAC;AAE7D;;GAEG;AACH,MAAM,OAAO,SAAS;IACZ,EAAE,CAAc;IAExB,YAAY,MAAe;QACzB,MAAM,GAAG,GAAG,MAAM,IAAI,OAAO,CAAC,GAAG,CAAC,gBAAgB,CAAC;QACnD,IAAI,CAAC,GAAG,EAAE,CAAC;YACT,MAAM,IAAI,KAAK,CAAC,qGAAqG,CAAC,CAAC;QACzH,CAAC;QACD,IAAI,CAAC,EAAE,GAAG,IAAI,WAAW,CAAC,EAAE,MAAM,EAAE,GAAG,EAAE,CAAC,CAAC;IAC7C,CAAC;IAED;;;;OAIG;IACH,KAAK,CAAC,WAAW,CAAC,WAAmB;QACnC,MAAM,MAAM,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA+BlB,CAAC;QAEE,IAAI,CAAC;YACH,MAAM,QAAQ,GAAG,MAAM,IAAI,CAAC,EAAE,CAAC,MAAM,CAAC,eAAe,CAAC;gBACpD,KAAK,EAAE,kBAAkB;gBACzB,QAAQ,EAAE,CAAC,MAAM,EAAE,WAAW,CAAC;gBAC/B,MAAM,EAAE;oBACN,gBAAgB,EAAE,kBAAkB;iBACrC;aACF,CAAC,CAAC;YAEH,IAAI,SAAS,GAAG,QAAQ,CAAC,IAAI,IAAI,IAAI,CAAC;YACtC,IAAI,SAAS,CAAC,QAAQ,CAAC,KAAK,CAAC,EAAE,CAAC;gBAC9B,SAAS,GAAG,SAAS,CAAC,OAAO,CAAC,UAAU,EAAE,EAAE,CAAC,CAAC,OAAO,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC;YAC3E,CAAC;YAED,OAAO,SAAS,CAAC;QACnB,CAAC;QAAC,OAAO,QAAa,EAAE,CAAC;YACvB,OAAO,CAAC,KAAK,CAAC,yBAAyB,QAAQ,CAAC,OAAO,EAAE,CAAC,CAAC;YAC3D,OAAO,IAAI,CAAC;QACd,CAAC;IACH,CAAC;CACF"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
interface SwaggerOptions {
|
|
2
|
+
title?: string | undefined;
|
|
3
|
+
version?: string | undefined;
|
|
4
|
+
desc?: string | undefined;
|
|
5
|
+
}
|
|
6
|
+
/**
|
|
7
|
+
* Writes an OpenAPI document as YAML or JSON to disk.
|
|
8
|
+
* @param outputPath Destination file path.
|
|
9
|
+
* @param pathsData Merged OpenAPI paths and components.
|
|
10
|
+
* @param options Title, version, and description metadata.
|
|
11
|
+
*/
|
|
12
|
+
export declare function writeSwaggerFile(outputPath: string, pathsData: Record<string, any>, options: SwaggerOptions): void;
|
|
13
|
+
export {};
|
|
14
|
+
//# sourceMappingURL=fileWriter.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"fileWriter.d.ts","sourceRoot":"","sources":["../../src/utils/fileWriter.ts"],"names":[],"mappings":"AAIA,UAAU,cAAc;IACtB,KAAK,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC3B,OAAO,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC7B,IAAI,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;CAC3B;AAED;;;;;GAKG;AACH,wBAAgB,gBAAgB,CAAC,UAAU,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,EAAE,OAAO,EAAE,cAAc,GAAG,IAAI,CAsClH"}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import * as fs from 'fs';
|
|
2
|
+
import * as path from 'path';
|
|
3
|
+
import yaml from 'yaml';
|
|
4
|
+
/**
|
|
5
|
+
* Writes an OpenAPI document as YAML or JSON to disk.
|
|
6
|
+
* @param outputPath Destination file path.
|
|
7
|
+
* @param pathsData Merged OpenAPI paths and components.
|
|
8
|
+
* @param options Title, version, and description metadata.
|
|
9
|
+
*/
|
|
10
|
+
export function writeSwaggerFile(outputPath, pathsData, options) {
|
|
11
|
+
let localTitle = 'DocuMind Generated API';
|
|
12
|
+
let localVersion = '1.0.0';
|
|
13
|
+
let localDesc = 'Automated OpenAPI documentation generated by DocuMind AI.';
|
|
14
|
+
try {
|
|
15
|
+
const localPackageJsonPath = path.resolve('./package.json');
|
|
16
|
+
if (fs.existsSync(localPackageJsonPath)) {
|
|
17
|
+
const pkg = JSON.parse(fs.readFileSync(localPackageJsonPath, 'utf8'));
|
|
18
|
+
if (pkg.name)
|
|
19
|
+
localTitle = `${pkg.name} API Documentation`;
|
|
20
|
+
if (pkg.version)
|
|
21
|
+
localVersion = pkg.version;
|
|
22
|
+
if (pkg.description)
|
|
23
|
+
localDesc = pkg.description;
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
catch (e) { }
|
|
27
|
+
const swaggerDoc = {
|
|
28
|
+
openapi: '3.0.0',
|
|
29
|
+
info: {
|
|
30
|
+
title: options.title || localTitle,
|
|
31
|
+
version: options.version || localVersion,
|
|
32
|
+
description: options.desc || localDesc
|
|
33
|
+
},
|
|
34
|
+
...pathsData
|
|
35
|
+
};
|
|
36
|
+
const fullPath = path.resolve(outputPath);
|
|
37
|
+
const fileExtension = path.extname(fullPath).toLowerCase();
|
|
38
|
+
let fileContent;
|
|
39
|
+
if (fileExtension === '.yaml' || fileExtension === '.yml') {
|
|
40
|
+
fileContent = yaml.stringify(swaggerDoc);
|
|
41
|
+
}
|
|
42
|
+
else {
|
|
43
|
+
fileContent = JSON.stringify(swaggerDoc, null, 2);
|
|
44
|
+
}
|
|
45
|
+
fs.writeFileSync(fullPath, fileContent, 'utf8');
|
|
46
|
+
console.log(`\nš Success! Swagger documentation saved to: ${fullPath}`);
|
|
47
|
+
}
|
|
48
|
+
//# sourceMappingURL=fileWriter.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"fileWriter.js","sourceRoot":"","sources":["../../src/utils/fileWriter.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,IAAI,CAAC;AACzB,OAAO,KAAK,IAAI,MAAM,MAAM,CAAC;AAC7B,OAAO,IAAI,MAAM,MAAM,CAAC;AAQxB;;;;;GAKG;AACH,MAAM,UAAU,gBAAgB,CAAC,UAAkB,EAAE,SAA8B,EAAE,OAAuB;IACxG,IAAI,UAAU,GAAG,wBAAwB,CAAC;IAC1C,IAAI,YAAY,GAAG,OAAO,CAAC;IAC3B,IAAI,SAAS,GAAG,2DAA2D,CAAC;IAE5E,IAAI,CAAC;QACD,MAAM,oBAAoB,GAAG,IAAI,CAAC,OAAO,CAAC,gBAAgB,CAAC,CAAC;QAC5D,IAAI,EAAE,CAAC,UAAU,CAAC,oBAAoB,CAAC,EAAE,CAAC;YACtC,MAAM,GAAG,GAAG,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC,YAAY,CAAC,oBAAoB,EAAE,MAAM,CAAC,CAAC,CAAC;YACtE,IAAI,GAAG,CAAC,IAAI;gBAAE,UAAU,GAAG,GAAG,GAAG,CAAC,IAAI,oBAAoB,CAAC;YAC3D,IAAI,GAAG,CAAC,OAAO;gBAAE,YAAY,GAAG,GAAG,CAAC,OAAO,CAAC;YAC5C,IAAI,GAAG,CAAC,WAAW;gBAAE,SAAS,GAAG,GAAG,CAAC,WAAW,CAAC;QACrD,CAAC;IACL,CAAC;IAAC,OAAO,CAAC,EAAE,CAAC,CAAA,CAAC;IAEd,MAAM,UAAU,GAAG;QACf,OAAO,EAAE,OAAO;QAChB,IAAI,EAAE;YACF,KAAK,EAAE,OAAO,CAAC,KAAK,IAAI,UAAU;YAClC,OAAO,EAAE,OAAO,CAAC,OAAO,IAAI,YAAY;YACxC,WAAW,EAAE,OAAO,CAAC,IAAI,IAAI,SAAS;SACzC;QACD,GAAG,SAAS;KACf,CAAC;IAEF,MAAM,QAAQ,GAAG,IAAI,CAAC,OAAO,CAAC,UAAU,CAAC,CAAC;IAC1C,MAAM,aAAa,GAAG,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC,WAAW,EAAE,CAAC;IAE3D,IAAI,WAAmB,CAAC;IAExB,IAAI,aAAa,KAAK,OAAO,IAAI,aAAa,KAAK,MAAM,EAAE,CAAC;QACxD,WAAW,GAAG,IAAI,CAAC,SAAS,CAAC,UAAU,CAAC,CAAC;IAC7C,CAAC;SAAM,CAAC;QACJ,WAAW,GAAG,IAAI,CAAC,SAAS,CAAC,UAAU,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC;IACtD,CAAC;IAED,EAAE,CAAC,aAAa,CAAC,QAAQ,EAAE,WAAW,EAAE,MAAM,CAAC,CAAC;IAChD,OAAO,CAAC,GAAG,CAAC,iDAAiD,QAAQ,EAAE,CAAC,CAAC;AAC7E,CAAC"}
|
package/package.json
ADDED
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@mena-emad/documind",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "AI-Powered Swagger/OpenAPI Documentation Generator for Express.js projects with intelligent context stitching.",
|
|
5
|
+
"main": "dist/index.js",
|
|
6
|
+
"types": "dist/index.d.ts",
|
|
7
|
+
"publishConfig": {
|
|
8
|
+
"access": "public"
|
|
9
|
+
},
|
|
10
|
+
"type": "module",
|
|
11
|
+
"bin": {
|
|
12
|
+
"documind": "./dist/index.js"
|
|
13
|
+
},
|
|
14
|
+
"files": [
|
|
15
|
+
"dist"
|
|
16
|
+
],
|
|
17
|
+
"scripts": {
|
|
18
|
+
"build": "tsc",
|
|
19
|
+
"dev": "tsx watch src/index.ts",
|
|
20
|
+
"prepublishOnly": "npm run build"
|
|
21
|
+
},
|
|
22
|
+
"keywords": [
|
|
23
|
+
"cli",
|
|
24
|
+
"swagger",
|
|
25
|
+
"openapi",
|
|
26
|
+
"express",
|
|
27
|
+
"gemini",
|
|
28
|
+
"ai",
|
|
29
|
+
"automation",
|
|
30
|
+
"documentation"
|
|
31
|
+
],
|
|
32
|
+
"author": {
|
|
33
|
+
"name": "Mena Emad Sawares",
|
|
34
|
+
"url": "https://menaemad.vercel.app/",
|
|
35
|
+
"email": "menaemad6543@gmail.com",
|
|
36
|
+
"github": "https://github.com/mena-emad"
|
|
37
|
+
},
|
|
38
|
+
"license": "ISC",
|
|
39
|
+
"engines": {
|
|
40
|
+
"node": ">=18.0.0"
|
|
41
|
+
},
|
|
42
|
+
"devDependencies": {
|
|
43
|
+
"@types/node": "^22.0.0",
|
|
44
|
+
"tsx": "^4.19.0",
|
|
45
|
+
"typescript": "^5.7.0"
|
|
46
|
+
},
|
|
47
|
+
"dependencies": {
|
|
48
|
+
"@google/genai": "^0.1.2",
|
|
49
|
+
"commander": "^13.1.0",
|
|
50
|
+
"dotenv": "^16.4.7",
|
|
51
|
+
"ora": "^8.1.1",
|
|
52
|
+
"yaml": "^2.7.0"
|
|
53
|
+
}
|
|
54
|
+
}
|