@veloxts/cli 0.6.57 → 0.6.58

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 CHANGED
@@ -1,5 +1,17 @@
1
1
  # @veloxts/cli
2
2
 
3
+ ## 0.6.58
4
+
5
+ ### Patch Changes
6
+
7
+ - feat(router): add OpenAPI 3.0.3 specification generator
8
+ - Updated dependencies
9
+ - @veloxts/auth@0.6.58
10
+ - @veloxts/core@0.6.58
11
+ - @veloxts/orm@0.6.58
12
+ - @veloxts/router@0.6.58
13
+ - @veloxts/validation@0.6.58
14
+
3
15
  ## 0.6.57
4
16
 
5
17
  ### Patch Changes
package/dist/cli.js CHANGED
@@ -15,6 +15,7 @@ import { createIntrospectCommand } from './commands/introspect.js';
15
15
  import { createMakeCommand } from './commands/make.js';
16
16
  import { createMcpCommand } from './commands/mcp.js';
17
17
  import { createMigrateCommand } from './commands/migrate.js';
18
+ import { createOpenApiCommand } from './commands/openapi.js';
18
19
  import { createProceduresCommand } from './commands/procedures.js';
19
20
  import { createScheduleCommand } from './commands/schedule.js';
20
21
  import { createTenantCommand } from './commands/tenant.js';
@@ -36,6 +37,7 @@ function createCLI() {
36
37
  program.addCommand(createMakeCommand());
37
38
  program.addCommand(createMcpCommand());
38
39
  program.addCommand(createMigrateCommand());
40
+ program.addCommand(createOpenApiCommand());
39
41
  program.addCommand(createProceduresCommand());
40
42
  program.addCommand(createScheduleCommand());
41
43
  program.addCommand(createTenantCommand());
@@ -0,0 +1,11 @@
1
+ /**
2
+ * OpenAPI command - Generate OpenAPI specifications
3
+ *
4
+ * Provides subcommands for generating OpenAPI documentation:
5
+ * - openapi:generate - Generate OpenAPI JSON specification from procedures
6
+ */
7
+ import { Command } from 'commander';
8
+ /**
9
+ * Create the openapi command with subcommands
10
+ */
11
+ export declare function createOpenApiCommand(): Command;
@@ -0,0 +1,205 @@
1
+ /**
2
+ * OpenAPI command - Generate OpenAPI specifications
3
+ *
4
+ * Provides subcommands for generating OpenAPI documentation:
5
+ * - openapi:generate - Generate OpenAPI JSON specification from procedures
6
+ */
7
+ import { existsSync, writeFileSync } from 'node:fs';
8
+ import { mkdir } from 'node:fs/promises';
9
+ import { dirname, resolve } from 'node:path';
10
+ import { discoverProceduresVerbose, generateOpenApiSpec, isDiscoveryError, validateOpenApiSpec, } from '@veloxts/router';
11
+ import { Command } from 'commander';
12
+ import { config as loadEnv } from 'dotenv';
13
+ import pc from 'picocolors';
14
+ /**
15
+ * Load environment variables from .env file if present
16
+ */
17
+ function loadEnvironment() {
18
+ const envPath = resolve(process.cwd(), '.env');
19
+ if (existsSync(envPath)) {
20
+ loadEnv({ path: envPath });
21
+ }
22
+ }
23
+ // ============================================================================
24
+ // Helper Functions
25
+ // ============================================================================
26
+ /**
27
+ * Parse server URLs into OpenAPI Server objects
28
+ */
29
+ function parseServers(servers) {
30
+ if (!servers || servers.length === 0) {
31
+ return [];
32
+ }
33
+ return servers.map((server, index) => {
34
+ // Support "url|description" format
35
+ const [url, description] = server.split('|');
36
+ return {
37
+ url: url.trim(),
38
+ description: description?.trim() ?? (index === 0 ? 'Primary server' : undefined),
39
+ };
40
+ });
41
+ }
42
+ /**
43
+ * Ensure directory exists for output file
44
+ */
45
+ async function ensureDir(filePath) {
46
+ const dir = dirname(filePath);
47
+ if (!existsSync(dir)) {
48
+ await mkdir(dir, { recursive: true });
49
+ }
50
+ }
51
+ /**
52
+ * Print success message with summary
53
+ */
54
+ function printSuccess(outputPath, spec, warnings, quiet) {
55
+ if (quiet) {
56
+ return;
57
+ }
58
+ const pathCount = Object.keys(spec.paths).length;
59
+ const tagCount = spec.tags?.length ?? 0;
60
+ console.log();
61
+ console.log(pc.green('✓') + pc.bold(' OpenAPI specification generated'));
62
+ console.log();
63
+ console.log(` ${pc.dim('Output:')} ${outputPath}`);
64
+ console.log(` ${pc.dim('Title:')} ${spec.info.title}`);
65
+ console.log(` ${pc.dim('Version:')} ${spec.info.version}`);
66
+ console.log(` ${pc.dim('Paths:')} ${pathCount}`);
67
+ console.log(` ${pc.dim('Tags:')} ${tagCount}`);
68
+ if (spec.servers?.length) {
69
+ console.log(` ${pc.dim('Servers:')} ${spec.servers.map((s) => s.url).join(', ')}`);
70
+ }
71
+ if (warnings.length > 0) {
72
+ console.log();
73
+ console.log(pc.yellow(`${warnings.length} warning(s):`));
74
+ for (const warning of warnings) {
75
+ console.log(pc.yellow(` • ${warning}`));
76
+ }
77
+ }
78
+ console.log();
79
+ }
80
+ // ============================================================================
81
+ // Command Implementation
82
+ // ============================================================================
83
+ /**
84
+ * Create the openapi:generate command
85
+ */
86
+ function createGenerateCommand() {
87
+ return new Command('generate')
88
+ .description('Generate OpenAPI specification from procedures')
89
+ .option('-p, --path <path>', 'Path to procedures directory', './src/procedures')
90
+ .option('-o, --output <file>', 'Output file path', './openapi.json')
91
+ .option('-t, --title <title>', 'API title', 'VeloxTS API')
92
+ .option('-V, --version <version>', 'API version', '1.0.0')
93
+ .option('-d, --description <desc>', 'API description')
94
+ .option('-s, --server <url>', 'Server URL (can be specified multiple times)', collectOption)
95
+ .option('--prefix <prefix>', 'API route prefix', '/api')
96
+ .option('-r, --recursive', 'Scan subdirectories for procedures', false)
97
+ .option('--pretty', 'Pretty-print JSON output', true)
98
+ .option('--no-pretty', 'Minify JSON output')
99
+ .option('--validate', 'Validate generated spec for issues', true)
100
+ .option('--no-validate', 'Skip validation')
101
+ .option('-q, --quiet', 'Suppress output except errors', false)
102
+ .action(async (options) => {
103
+ // Load .env file before importing procedure files
104
+ loadEnvironment();
105
+ const proceduresPath = options.path ?? './src/procedures';
106
+ const outputPath = resolve(process.cwd(), options.output ?? './openapi.json');
107
+ const discoveryOptions = {
108
+ recursive: options.recursive ?? false,
109
+ onInvalidExport: 'warn',
110
+ };
111
+ try {
112
+ // Discover procedures
113
+ if (!options.quiet) {
114
+ console.log(pc.dim('Discovering procedures...'));
115
+ }
116
+ const discovery = await discoverProceduresVerbose(proceduresPath, discoveryOptions);
117
+ if (discovery.collections.length === 0) {
118
+ console.error(pc.red('Error: No procedure collections found'));
119
+ console.error(pc.dim(`Searched in: ${proceduresPath}`));
120
+ process.exit(1);
121
+ }
122
+ if (!options.quiet) {
123
+ console.log(pc.dim(`Found ${discovery.collections.length} collection(s) with ` +
124
+ `${discovery.collections.reduce((sum, c) => sum + Object.keys(c.procedures).length, 0)} procedure(s)`));
125
+ }
126
+ // Build OpenAPI options
127
+ const openApiOptions = {
128
+ info: {
129
+ title: options.title ?? 'VeloxTS API',
130
+ version: options.version ?? '1.0.0',
131
+ description: options.description,
132
+ },
133
+ prefix: options.prefix ?? '/api',
134
+ servers: parseServers(options.server),
135
+ };
136
+ // Generate spec
137
+ if (!options.quiet) {
138
+ console.log(pc.dim('Generating OpenAPI specification...'));
139
+ }
140
+ const spec = generateOpenApiSpec(discovery.collections, openApiOptions);
141
+ // Validate if requested
142
+ const warnings = [];
143
+ if (options.validate !== false) {
144
+ const validationWarnings = validateOpenApiSpec(spec);
145
+ warnings.push(...validationWarnings);
146
+ }
147
+ // Add discovery warnings
148
+ warnings.push(...discovery.warnings.map((w) => `${w.filePath}: ${w.message}`));
149
+ // Write output
150
+ await ensureDir(outputPath);
151
+ const jsonContent = options.pretty !== false ? JSON.stringify(spec, null, 2) : JSON.stringify(spec);
152
+ writeFileSync(outputPath, jsonContent, 'utf-8');
153
+ // Print success message
154
+ printSuccess(outputPath, spec, warnings, options.quiet ?? false);
155
+ // Exit explicitly - dynamic imports may keep event loop running
156
+ process.exit(0);
157
+ }
158
+ catch (error) {
159
+ if (isDiscoveryError(error)) {
160
+ console.error(pc.red(error.format()));
161
+ process.exit(1);
162
+ }
163
+ throw error;
164
+ }
165
+ });
166
+ }
167
+ /**
168
+ * Collect multiple option values into array
169
+ */
170
+ function collectOption(value, previous = []) {
171
+ return [...previous, value];
172
+ }
173
+ /**
174
+ * Create the openapi:serve command (placeholder for future Swagger UI serving)
175
+ */
176
+ function createServeCommand() {
177
+ return new Command('serve')
178
+ .description('Start a local Swagger UI server (coming soon)')
179
+ .option('-f, --file <file>', 'OpenAPI spec file', './openapi.json')
180
+ .option('--port <port>', 'Server port', '8080')
181
+ .action((_options) => {
182
+ console.log(pc.yellow('The serve command will be available in a future version.'));
183
+ console.log(pc.dim('For now, use the swaggerUIPlugin in your Fastify app:'));
184
+ console.log();
185
+ console.log(pc.cyan(` import { swaggerUIPlugin } from '@veloxts/router';`));
186
+ console.log();
187
+ console.log(pc.cyan(` app.register(swaggerUIPlugin, {`));
188
+ console.log(pc.cyan(` routePrefix: '/docs',`));
189
+ console.log(pc.cyan(` collections: [userProcedures],`));
190
+ console.log(pc.cyan(` openapi: { info: { title: 'My API', version: '1.0.0' } },`));
191
+ console.log(pc.cyan(` });`));
192
+ console.log();
193
+ process.exit(0);
194
+ });
195
+ }
196
+ /**
197
+ * Create the openapi command with subcommands
198
+ */
199
+ export function createOpenApiCommand() {
200
+ const openapi = new Command('openapi')
201
+ .description('OpenAPI specification generation and management')
202
+ .addCommand(createGenerateCommand())
203
+ .addCommand(createServeCommand());
204
+ return openapi;
205
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@veloxts/cli",
3
- "version": "0.6.57",
3
+ "version": "0.6.58",
4
4
  "description": "Developer tooling and CLI commands for VeloxTS framework",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -40,11 +40,11 @@
40
40
  "picocolors": "1.1.1",
41
41
  "pluralize": "8.0.0",
42
42
  "tsx": "4.21.0",
43
- "@veloxts/core": "0.6.57",
44
- "@veloxts/auth": "0.6.57",
45
- "@veloxts/orm": "0.6.57",
46
- "@veloxts/router": "0.6.57",
47
- "@veloxts/validation": "0.6.57"
43
+ "@veloxts/auth": "0.6.58",
44
+ "@veloxts/core": "0.6.58",
45
+ "@veloxts/router": "0.6.58",
46
+ "@veloxts/orm": "0.6.58",
47
+ "@veloxts/validation": "0.6.58"
48
48
  },
49
49
  "peerDependencies": {
50
50
  "@prisma/client": ">=7.0.0"