@veloxts/cli 0.6.85 → 0.6.86
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/CHANGELOG.md +12 -0
- package/GUIDE.md +99 -0
- package/dist/commands/openapi.d.ts +4 -3
- package/dist/commands/openapi.js +163 -28
- package/package.json +7 -6
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,17 @@
|
|
|
1
1
|
# @veloxts/cli
|
|
2
2
|
|
|
3
|
+
## 0.6.86
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- updated documentation
|
|
8
|
+
- Updated dependencies
|
|
9
|
+
- @veloxts/auth@0.6.86
|
|
10
|
+
- @veloxts/core@0.6.86
|
|
11
|
+
- @veloxts/orm@0.6.86
|
|
12
|
+
- @veloxts/router@0.6.86
|
|
13
|
+
- @veloxts/validation@0.6.86
|
|
14
|
+
|
|
3
15
|
## 0.6.85
|
|
4
16
|
|
|
5
17
|
### Patch Changes
|
package/GUIDE.md
CHANGED
|
@@ -111,6 +111,105 @@ Supported platforms:
|
|
|
111
111
|
- Windows: `%APPDATA%\Claude\claude_desktop_config.json`
|
|
112
112
|
- Linux: `~/.config/Claude/claude_desktop_config.json`
|
|
113
113
|
|
|
114
|
+
### velox openapi generate
|
|
115
|
+
|
|
116
|
+
Generate OpenAPI 3.0.3 specification from procedure definitions:
|
|
117
|
+
|
|
118
|
+
```bash
|
|
119
|
+
# Basic usage - JSON to ./openapi.json
|
|
120
|
+
velox openapi generate
|
|
121
|
+
|
|
122
|
+
# YAML output (auto-detected from extension)
|
|
123
|
+
velox openapi generate -o ./docs/api.yaml
|
|
124
|
+
|
|
125
|
+
# Full configuration
|
|
126
|
+
velox openapi generate \
|
|
127
|
+
--path ./src/procedures \
|
|
128
|
+
--output ./docs/openapi.json \
|
|
129
|
+
--title "My API" \
|
|
130
|
+
--version "2.0.0" \
|
|
131
|
+
--description "Production API documentation" \
|
|
132
|
+
--server "http://localhost:3030|Development" \
|
|
133
|
+
--server "https://api.example.com|Production" \
|
|
134
|
+
--prefix /api \
|
|
135
|
+
--recursive \
|
|
136
|
+
--pretty
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
**Options:**
|
|
140
|
+
|
|
141
|
+
| Option | Description | Default |
|
|
142
|
+
|--------|-------------|---------|
|
|
143
|
+
| `-p, --path <path>` | Procedures directory | `./src/procedures` |
|
|
144
|
+
| `-o, --output <file>` | Output file path | `./openapi.json` |
|
|
145
|
+
| `-f, --format <format>` | Output format: `json` or `yaml` | Auto-detected from extension |
|
|
146
|
+
| `-t, --title <title>` | API title | `VeloxTS API` |
|
|
147
|
+
| `-V, --version <version>` | API version | `1.0.0` |
|
|
148
|
+
| `-d, --description <desc>` | API description | None |
|
|
149
|
+
| `-s, --server <url>` | Server URL (repeatable, format: `url\|description`) | None |
|
|
150
|
+
| `--prefix <prefix>` | API route prefix | `/api` |
|
|
151
|
+
| `-r, --recursive` | Scan subdirectories for procedures | `false` |
|
|
152
|
+
| `--pretty` / `--no-pretty` | Pretty-print or minify output | `true` |
|
|
153
|
+
| `--validate` / `--no-validate` | Validate generated spec for issues | `true` |
|
|
154
|
+
| `-q, --quiet` | Suppress output except errors | `false` |
|
|
155
|
+
|
|
156
|
+
**Server URL Format:**
|
|
157
|
+
|
|
158
|
+
You can specify multiple server URLs using the `url|description` format:
|
|
159
|
+
|
|
160
|
+
```bash
|
|
161
|
+
velox openapi generate \
|
|
162
|
+
-s "http://localhost:3030|Local development" \
|
|
163
|
+
-s "https://staging.example.com|Staging environment" \
|
|
164
|
+
-s "https://api.example.com|Production"
|
|
165
|
+
```
|
|
166
|
+
|
|
167
|
+
**Output:**
|
|
168
|
+
|
|
169
|
+
The command generates a complete OpenAPI 3.0.3 specification including:
|
|
170
|
+
- Auto-generated paths from procedure naming conventions
|
|
171
|
+
- Request/response schemas from Zod definitions
|
|
172
|
+
- Security schemes from guards (JWT, API keys, etc.)
|
|
173
|
+
- Deprecation warnings from `.deprecated()` procedures
|
|
174
|
+
- Field descriptions from Zod `.describe()` calls
|
|
175
|
+
|
|
176
|
+
### velox openapi serve
|
|
177
|
+
|
|
178
|
+
Start a local Swagger UI server to preview OpenAPI documentation:
|
|
179
|
+
|
|
180
|
+
```bash
|
|
181
|
+
# Serve with default settings
|
|
182
|
+
velox openapi serve
|
|
183
|
+
|
|
184
|
+
# Custom spec file and port
|
|
185
|
+
velox openapi serve -f ./docs/api.yaml --port 9000
|
|
186
|
+
|
|
187
|
+
# Enable hot-reload on file changes
|
|
188
|
+
velox openapi serve --watch
|
|
189
|
+
|
|
190
|
+
# Bind to all interfaces (accessible from network)
|
|
191
|
+
velox openapi serve --host 0.0.0.0
|
|
192
|
+
```
|
|
193
|
+
|
|
194
|
+
**Options:**
|
|
195
|
+
|
|
196
|
+
| Option | Description | Default |
|
|
197
|
+
|--------|-------------|---------|
|
|
198
|
+
| `-f, --file <file>` | OpenAPI spec file (JSON or YAML) | `./openapi.json` |
|
|
199
|
+
| `--port <port>` | Server port | `8080` |
|
|
200
|
+
| `--host <host>` | Host to bind | `localhost` |
|
|
201
|
+
| `-w, --watch` | Watch for file changes and hot-reload | `false` |
|
|
202
|
+
|
|
203
|
+
**Features:**
|
|
204
|
+
- Interactive Swagger UI interface
|
|
205
|
+
- Try out API endpoints directly from the browser
|
|
206
|
+
- Auto-reload when spec file changes (with `--watch`)
|
|
207
|
+
- CORS enabled for development
|
|
208
|
+
|
|
209
|
+
The server provides two endpoints:
|
|
210
|
+
- `/` - Swagger UI interface
|
|
211
|
+
- `/openapi.json` - Raw OpenAPI specification (always JSON, regardless of source format)
|
|
212
|
+
|
|
114
213
|
## Database Seeding
|
|
115
214
|
|
|
116
215
|
### Creating Seeders
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* OpenAPI command - Generate OpenAPI specifications
|
|
2
|
+
* OpenAPI command - Generate and serve OpenAPI specifications
|
|
3
3
|
*
|
|
4
|
-
* Provides subcommands for generating OpenAPI documentation:
|
|
5
|
-
* - openapi
|
|
4
|
+
* Provides subcommands for generating and serving OpenAPI documentation:
|
|
5
|
+
* - openapi generate - Generate OpenAPI JSON/YAML specification from procedures
|
|
6
|
+
* - openapi serve - Start a local Swagger UI server
|
|
6
7
|
*/
|
|
7
8
|
import { Command } from 'commander';
|
|
8
9
|
/**
|
package/dist/commands/openapi.js
CHANGED
|
@@ -1,16 +1,19 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* OpenAPI command - Generate OpenAPI specifications
|
|
2
|
+
* OpenAPI command - Generate and serve OpenAPI specifications
|
|
3
3
|
*
|
|
4
|
-
* Provides subcommands for generating OpenAPI documentation:
|
|
5
|
-
* - openapi
|
|
4
|
+
* Provides subcommands for generating and serving OpenAPI documentation:
|
|
5
|
+
* - openapi generate - Generate OpenAPI JSON/YAML specification from procedures
|
|
6
|
+
* - openapi serve - Start a local Swagger UI server
|
|
6
7
|
*/
|
|
7
|
-
import { existsSync, writeFileSync } from 'node:fs';
|
|
8
|
+
import { existsSync, readFileSync, watch, writeFileSync } from 'node:fs';
|
|
8
9
|
import { mkdir } from 'node:fs/promises';
|
|
9
|
-
import {
|
|
10
|
-
import {
|
|
10
|
+
import { createServer } from 'node:http';
|
|
11
|
+
import { dirname, extname, resolve } from 'node:path';
|
|
12
|
+
import { discoverProceduresVerbose, generateOpenApiSpec, generateSwaggerUIHtml, isDiscoveryError, validateOpenApiSpec, } from '@veloxts/router';
|
|
11
13
|
import { Command } from 'commander';
|
|
12
14
|
import { config as loadEnv } from 'dotenv';
|
|
13
15
|
import pc from 'picocolors';
|
|
16
|
+
import YAML from 'yaml';
|
|
14
17
|
/**
|
|
15
18
|
* Load environment variables from .env file if present
|
|
16
19
|
*/
|
|
@@ -23,6 +26,28 @@ function loadEnvironment() {
|
|
|
23
26
|
// ============================================================================
|
|
24
27
|
// Helper Functions
|
|
25
28
|
// ============================================================================
|
|
29
|
+
/**
|
|
30
|
+
* Detect output format from file extension
|
|
31
|
+
*/
|
|
32
|
+
function detectFormat(outputPath, explicitFormat) {
|
|
33
|
+
if (explicitFormat) {
|
|
34
|
+
return explicitFormat;
|
|
35
|
+
}
|
|
36
|
+
const ext = extname(outputPath).toLowerCase();
|
|
37
|
+
if (ext === '.yaml' || ext === '.yml') {
|
|
38
|
+
return 'yaml';
|
|
39
|
+
}
|
|
40
|
+
return 'json';
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Serialize OpenAPI spec to string
|
|
44
|
+
*/
|
|
45
|
+
function serializeSpec(spec, format, pretty) {
|
|
46
|
+
if (format === 'yaml') {
|
|
47
|
+
return YAML.stringify(spec, { indent: 2 });
|
|
48
|
+
}
|
|
49
|
+
return pretty ? JSON.stringify(spec, null, 2) : JSON.stringify(spec);
|
|
50
|
+
}
|
|
26
51
|
/**
|
|
27
52
|
* Parse server URLs into OpenAPI Server objects
|
|
28
53
|
*/
|
|
@@ -51,7 +76,7 @@ async function ensureDir(filePath) {
|
|
|
51
76
|
/**
|
|
52
77
|
* Print success message with summary
|
|
53
78
|
*/
|
|
54
|
-
function printSuccess(outputPath, spec, warnings, quiet) {
|
|
79
|
+
function printSuccess(outputPath, spec, warnings, quiet, format = 'json') {
|
|
55
80
|
if (quiet) {
|
|
56
81
|
return;
|
|
57
82
|
}
|
|
@@ -61,6 +86,7 @@ function printSuccess(outputPath, spec, warnings, quiet) {
|
|
|
61
86
|
console.log(pc.green('✓') + pc.bold(' OpenAPI specification generated'));
|
|
62
87
|
console.log();
|
|
63
88
|
console.log(` ${pc.dim('Output:')} ${outputPath}`);
|
|
89
|
+
console.log(` ${pc.dim('Format:')} ${format.toUpperCase()}`);
|
|
64
90
|
console.log(` ${pc.dim('Title:')} ${spec.info.title}`);
|
|
65
91
|
console.log(` ${pc.dim('Version:')} ${spec.info.version}`);
|
|
66
92
|
console.log(` ${pc.dim('Paths:')} ${pathCount}`);
|
|
@@ -88,14 +114,15 @@ function createGenerateCommand() {
|
|
|
88
114
|
.description('Generate OpenAPI specification from procedures')
|
|
89
115
|
.option('-p, --path <path>', 'Path to procedures directory', './src/procedures')
|
|
90
116
|
.option('-o, --output <file>', 'Output file path', './openapi.json')
|
|
117
|
+
.option('-f, --format <format>', 'Output format (json or yaml), auto-detected from file extension if not specified')
|
|
91
118
|
.option('-t, --title <title>', 'API title', 'VeloxTS API')
|
|
92
119
|
.option('-V, --version <version>', 'API version', '1.0.0')
|
|
93
120
|
.option('-d, --description <desc>', 'API description')
|
|
94
121
|
.option('-s, --server <url>', 'Server URL (can be specified multiple times)', collectOption)
|
|
95
122
|
.option('--prefix <prefix>', 'API route prefix', '/api')
|
|
96
123
|
.option('-r, --recursive', 'Scan subdirectories for procedures', false)
|
|
97
|
-
.option('--pretty', 'Pretty-print
|
|
98
|
-
.option('--no-pretty', 'Minify
|
|
124
|
+
.option('--pretty', 'Pretty-print output', true)
|
|
125
|
+
.option('--no-pretty', 'Minify output')
|
|
99
126
|
.option('--validate', 'Validate generated spec for issues', true)
|
|
100
127
|
.option('--no-validate', 'Skip validation')
|
|
101
128
|
.option('-q, --quiet', 'Suppress output except errors', false)
|
|
@@ -146,12 +173,14 @@ function createGenerateCommand() {
|
|
|
146
173
|
}
|
|
147
174
|
// Add discovery warnings
|
|
148
175
|
warnings.push(...discovery.warnings.map((w) => `${w.filePath}: ${w.message}`));
|
|
176
|
+
// Determine output format
|
|
177
|
+
const format = detectFormat(outputPath, options.format);
|
|
149
178
|
// Write output
|
|
150
179
|
await ensureDir(outputPath);
|
|
151
|
-
const
|
|
152
|
-
writeFileSync(outputPath,
|
|
180
|
+
const content = serializeSpec(spec, format, options.pretty !== false);
|
|
181
|
+
writeFileSync(outputPath, content, 'utf-8');
|
|
153
182
|
// Print success message
|
|
154
|
-
printSuccess(outputPath, spec, warnings, options.quiet ?? false);
|
|
183
|
+
printSuccess(outputPath, spec, warnings, options.quiet ?? false, format);
|
|
155
184
|
// Exit explicitly - dynamic imports may keep event loop running
|
|
156
185
|
process.exit(0);
|
|
157
186
|
}
|
|
@@ -171,28 +200,134 @@ function collectOption(value, previous = []) {
|
|
|
171
200
|
return [...previous, value];
|
|
172
201
|
}
|
|
173
202
|
/**
|
|
174
|
-
* Create the openapi:serve command
|
|
203
|
+
* Create the openapi:serve command
|
|
175
204
|
*/
|
|
176
205
|
function createServeCommand() {
|
|
177
206
|
return new Command('serve')
|
|
178
|
-
.description('Start a local Swagger UI server
|
|
179
|
-
.option('-f, --file <file>', 'OpenAPI spec file', './openapi.json')
|
|
207
|
+
.description('Start a local Swagger UI server to preview OpenAPI documentation')
|
|
208
|
+
.option('-f, --file <file>', 'OpenAPI spec file (JSON or YAML)', './openapi.json')
|
|
180
209
|
.option('--port <port>', 'Server port', '8080')
|
|
181
|
-
.
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
210
|
+
.option('--host <host>', 'Host to bind', 'localhost')
|
|
211
|
+
.option('-w, --watch', 'Watch for file changes and hot-reload', false)
|
|
212
|
+
.action(async (options) => {
|
|
213
|
+
const filePath = resolve(process.cwd(), options.file ?? './openapi.json');
|
|
214
|
+
const port = parseInt(options.port ?? '8080', 10);
|
|
215
|
+
const host = options.host ?? 'localhost';
|
|
216
|
+
const watchMode = options.watch ?? false;
|
|
217
|
+
// Validate port number
|
|
218
|
+
if (Number.isNaN(port) || port < 1 || port > 65535) {
|
|
219
|
+
console.error(pc.red(`Error: Invalid port number: ${options.port}. Must be between 1-65535.`));
|
|
220
|
+
process.exit(1);
|
|
221
|
+
}
|
|
222
|
+
// Verify spec file exists
|
|
223
|
+
if (!existsSync(filePath)) {
|
|
224
|
+
console.error(pc.red(`Error: OpenAPI spec file not found: ${filePath}`));
|
|
225
|
+
console.log(pc.dim('Run `velox openapi generate` first to create the spec.'));
|
|
226
|
+
process.exit(1);
|
|
227
|
+
}
|
|
228
|
+
// Load spec file
|
|
229
|
+
let spec;
|
|
230
|
+
try {
|
|
231
|
+
spec = loadSpecFile(filePath);
|
|
232
|
+
}
|
|
233
|
+
catch (error) {
|
|
234
|
+
console.error(pc.red(`Error: Failed to parse OpenAPI spec: ${error.message}`));
|
|
235
|
+
process.exit(1);
|
|
236
|
+
}
|
|
237
|
+
// Generate Swagger UI HTML
|
|
238
|
+
const title = spec.info?.title ?? 'API Documentation';
|
|
239
|
+
let htmlContent = generateSwaggerUIHtml({
|
|
240
|
+
specUrl: '/openapi.json',
|
|
241
|
+
title,
|
|
242
|
+
config: { tryItOutEnabled: true },
|
|
243
|
+
});
|
|
244
|
+
// Create HTTP server
|
|
245
|
+
const server = createServer((req, res) => {
|
|
246
|
+
// Handle CORS
|
|
247
|
+
res.setHeader('Access-Control-Allow-Origin', '*');
|
|
248
|
+
if (req.url === '/' || req.url === '/index.html') {
|
|
249
|
+
res.writeHead(200, { 'Content-Type': 'text/html; charset=utf-8' });
|
|
250
|
+
res.end(htmlContent);
|
|
251
|
+
}
|
|
252
|
+
else if (req.url === '/openapi.json') {
|
|
253
|
+
res.writeHead(200, { 'Content-Type': 'application/json' });
|
|
254
|
+
res.end(JSON.stringify(spec, null, 2));
|
|
255
|
+
}
|
|
256
|
+
else {
|
|
257
|
+
res.writeHead(404, { 'Content-Type': 'text/plain' });
|
|
258
|
+
res.end('Not Found');
|
|
259
|
+
}
|
|
260
|
+
});
|
|
261
|
+
// Watch for file changes
|
|
262
|
+
let watcher;
|
|
263
|
+
let debounceTimer;
|
|
264
|
+
if (watchMode) {
|
|
265
|
+
watcher = watch(filePath, () => {
|
|
266
|
+
// Debounce rapid changes
|
|
267
|
+
if (debounceTimer) {
|
|
268
|
+
clearTimeout(debounceTimer);
|
|
269
|
+
}
|
|
270
|
+
debounceTimer = setTimeout(() => {
|
|
271
|
+
try {
|
|
272
|
+
spec = loadSpecFile(filePath);
|
|
273
|
+
htmlContent = generateSwaggerUIHtml({
|
|
274
|
+
specUrl: '/openapi.json',
|
|
275
|
+
title: spec.info?.title ?? 'API Documentation',
|
|
276
|
+
config: { tryItOutEnabled: true },
|
|
277
|
+
});
|
|
278
|
+
console.log(pc.green('✓') + pc.dim(' Spec reloaded'));
|
|
279
|
+
}
|
|
280
|
+
catch (error) {
|
|
281
|
+
console.error(pc.yellow('⚠') + pc.dim(` Failed to reload spec: ${error.message}`));
|
|
282
|
+
}
|
|
283
|
+
finally {
|
|
284
|
+
debounceTimer = undefined;
|
|
285
|
+
}
|
|
286
|
+
}, 100);
|
|
287
|
+
});
|
|
288
|
+
}
|
|
289
|
+
// Handle graceful shutdown
|
|
290
|
+
const shutdown = () => {
|
|
291
|
+
console.log();
|
|
292
|
+
console.log(pc.dim('Shutting down...'));
|
|
293
|
+
if (debounceTimer) {
|
|
294
|
+
clearTimeout(debounceTimer);
|
|
295
|
+
}
|
|
296
|
+
watcher?.close();
|
|
297
|
+
server.close(() => {
|
|
298
|
+
process.exit(0);
|
|
299
|
+
});
|
|
300
|
+
};
|
|
301
|
+
process.on('SIGINT', shutdown);
|
|
302
|
+
process.on('SIGTERM', shutdown);
|
|
303
|
+
// Start server
|
|
304
|
+
server.listen(port, host, () => {
|
|
305
|
+
console.log();
|
|
306
|
+
console.log(pc.green('✓') + pc.bold(' Swagger UI server started'));
|
|
307
|
+
console.log();
|
|
308
|
+
console.log(` ${pc.dim('URL:')} http://${host}:${port}`);
|
|
309
|
+
console.log(` ${pc.dim('Spec:')} ${filePath}`);
|
|
310
|
+
console.log(` ${pc.dim('Title:')} ${title}`);
|
|
311
|
+
if (watchMode) {
|
|
312
|
+
console.log(` ${pc.dim('Watch:')} ${pc.green('enabled')}`);
|
|
313
|
+
}
|
|
314
|
+
console.log();
|
|
315
|
+
console.log(pc.dim('Press Ctrl+C to stop'));
|
|
316
|
+
console.log();
|
|
317
|
+
});
|
|
194
318
|
});
|
|
195
319
|
}
|
|
320
|
+
/**
|
|
321
|
+
* Load and parse an OpenAPI spec file (JSON or YAML)
|
|
322
|
+
*/
|
|
323
|
+
function loadSpecFile(filePath) {
|
|
324
|
+
const content = readFileSync(filePath, 'utf-8');
|
|
325
|
+
const ext = extname(filePath).toLowerCase();
|
|
326
|
+
if (ext === '.yaml' || ext === '.yml') {
|
|
327
|
+
return YAML.parse(content);
|
|
328
|
+
}
|
|
329
|
+
return JSON.parse(content);
|
|
330
|
+
}
|
|
196
331
|
/**
|
|
197
332
|
* Create the openapi command with subcommands
|
|
198
333
|
*/
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@veloxts/cli",
|
|
3
|
-
"version": "0.6.
|
|
3
|
+
"version": "0.6.86",
|
|
4
4
|
"description": "Developer tooling and CLI commands for VeloxTS framework",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -40,11 +40,12 @@
|
|
|
40
40
|
"picocolors": "1.1.1",
|
|
41
41
|
"pluralize": "8.0.0",
|
|
42
42
|
"tsx": "4.21.0",
|
|
43
|
-
"
|
|
44
|
-
"@veloxts/
|
|
45
|
-
"@veloxts/router": "0.6.
|
|
46
|
-
"@veloxts/validation": "0.6.
|
|
47
|
-
"@veloxts/
|
|
43
|
+
"yaml": "2.8.0",
|
|
44
|
+
"@veloxts/orm": "0.6.86",
|
|
45
|
+
"@veloxts/router": "0.6.86",
|
|
46
|
+
"@veloxts/validation": "0.6.86",
|
|
47
|
+
"@veloxts/auth": "0.6.86",
|
|
48
|
+
"@veloxts/core": "0.6.86"
|
|
48
49
|
},
|
|
49
50
|
"peerDependencies": {
|
|
50
51
|
"@prisma/client": ">=7.0.0"
|