@boltic/cli 1.0.6-beta.12 → 1.0.6-beta.14

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 CHANGED
@@ -1,149 +1,505 @@
1
1
  # ⚡ Boltic CLI
2
2
 
3
- > A powerful CLI tool for creating, managing, and publishing Boltic integrations.
3
+ > **Professional CLI tool for creating, managing, and publishing Boltic Workflow integrations with enterprise-grade features and seamless developer experience.**
4
4
 
5
5
  [![NPM Version](https://img.shields.io/npm/v/@boltic/cli)](https://www.npmjs.com/package/@boltic/cli)
6
- [![GitHub Repo](https://img.shields.io/badge/GitHub-Repo-blue?logo=github)](https://github.com/<your-username>/<your-repo>)
6
+ [![GitHub Repo](https://img.shields.io/badge/GitHub-Repo-blue?logo=github)](https://github.com/bolticio/cli)
7
7
  [![License](https://img.shields.io/npm/l/@boltic/cli)](./LICENSE)
8
8
  [![Node.js Package](https://github.com/bolticio/cli/actions/workflows/npm-publish.yml/badge.svg)](https://github.com/bolticio/cli/actions/workflows/npm-publish.yml)
9
+ [![PRs Welcome](https://img.shields.io/badge/PRs-welcome-brightgreen.svg)](http://makeapullrequest.com)
10
+ [![Maintenance](https://img.shields.io/badge/Maintained%3F-yes-green.svg)](https://github.com/bolticio/cli/graphs/commit-activity)
11
+
12
+ <div align="center">
13
+
14
+ ![Boltic CLI](https://img.shields.io/badge/Boltic-CLI-00D4AA?style=for-the-badge&logo=node.js&logoColor=white)
15
+
16
+ **Streamline your integration development workflow with Boltic CLI**
17
+
18
+ [Features](#-features) • [Installation](#-installation) • [Quick Start](#-quick-start) • [Documentation](#-documentation) • [Contributing](#-contributing)
19
+
20
+ </div>
21
+
22
+ ---
23
+
24
+ ## 📋 Table of Contents
25
+
26
+ - [✨ Features](#-features)
27
+ - [🚀 Quick Start](#-quick-start)
28
+ - [📦 Installation](#-installation)
29
+ - [🔐 Authentication](#-authentication)
30
+ - [🧩 Integration Management](#-integration-management)
31
+ - [📚 Command Reference](#-command-reference)
32
+ - [🛠️ Development Workflow](#️-development-workflow)
33
+ - [🔧 Configuration](#-configuration)
34
+ - [🛡️ Security](#-security)
35
+ - [🐛 Troubleshooting](#-troubleshooting)
36
+ - [📖 Documentation](#-documentation)
37
+ - [🤝 Contributing](#-contributing)
38
+ - [📄 License](#-license)
39
+
40
+ ---
41
+
42
+ ## ✨ Features
43
+
44
+ - 🔐 **Secure Authentication** - Enterprise-grade token management with secure storage
45
+ - 🚀 **Rapid Integration Development** - Create integrations in minutes, not hours
46
+ - 📦 **Smart Project Management** - Automated folder structure and configuration
47
+ - 🔄 **Real-time Synchronization** - Instant sync with Boltic Cloud platform
48
+ - 🎯 **Type-safe Development** - Support for Workflow Activities and Triggers
49
+ - 🎨 **Rich Interactive UI** - Beautiful command-line interface with progress indicators
50
+ - 📊 **Comprehensive Validation** - Built-in validation for all integration components
51
+ - 🔧 **Developer Experience** - Hot reload, debugging tools, and comprehensive error handling
52
+ - 🌐 **Multi-platform Support** - Works seamlessly on Windows, macOS, and Linux
53
+ - 📈 **Version Control Integration** - Git-friendly workflow with proper ignore patterns
54
+
55
+ ---
56
+
57
+ ## 🚀 Quick Start
58
+
59
+ Get up and running with Boltic CLI in under 2 minutes:
60
+
61
+ ```bash
62
+ # Install Boltic CLI globally
63
+ npm install -g @boltic/cli
64
+
65
+ # Authenticate with your Boltic account
66
+ boltic login
67
+
68
+ # Create your first integration
69
+ boltic integration create
70
+
71
+ # Sync your changes
72
+ boltic integration sync
73
+
74
+ # Submit for review
75
+ boltic integration submit
76
+ ```
9
77
 
10
78
  ---
11
79
 
12
80
  ## 📦 Installation
13
81
 
14
- Install Boltic CLI globally via NPM:
82
+ ### Prerequisites
83
+
84
+ - **Node.js** 18.0.0 or higher
85
+ - **npm** 8.0.0 or higher
86
+ - **Git** (for version control)
87
+
88
+ ### Global Installation (Recommended)
15
89
 
16
90
  ```bash
17
91
  npm install -g @boltic/cli
18
92
  ```
19
93
 
94
+ ### Verify Installation
95
+
96
+ ```bash
97
+ boltic --version
98
+ ```
99
+
100
+ ### Development Installation
101
+
102
+ For contributors and developers:
103
+
104
+ ```bash
105
+ # Clone the repository
106
+ git clone https://github.com/bolticio/cli.git
107
+ cd cli
108
+
109
+ # Install dependencies
110
+ npm install
111
+
112
+ # Link for development
113
+ npm link
114
+
115
+ # Run tests
116
+ npm test
117
+ ```
118
+
20
119
  ---
21
120
 
22
121
  ## 🔐 Authentication
23
122
 
24
- To log in:
123
+ Boltic CLI uses a secure OAuth 2.0 flow with browser-based authentication for enhanced security and user experience.
124
+
125
+ ### Initial Login
25
126
 
26
127
  ```bash
27
128
  boltic login
28
129
  ```
29
130
 
30
- Follow the interactive prompt to enter your credentials. Your token will be stored securely for future use.
131
+ The authentication process follows these steps:
132
+
133
+ 1. **Browser Launch**: CLI automatically opens your default browser to the Boltic login page
134
+ 2. **OAuth Flow**: Complete the authentication in your browser (email/password or SSO)
135
+ 3. **Token Exchange**: CLI automatically exchanges the authorization code for access tokens
136
+ 4. **Secure Storage**: Tokens are encrypted and stored in your system's keychain
137
+
138
+ ### Authentication Flow
139
+
140
+ ```bash
141
+ # Start authentication
142
+ boltic login
143
+
144
+ # CLI will:
145
+ # 1. Generate a unique request code
146
+ # 2. Open browser to: https://console.fynd.com/auth/sign-in
147
+ # 3. Wait for you to complete login in browser
148
+ # 4. Poll for session data (up to 5 minutes)
149
+ # 5. Exchange session for bearer token
150
+ # 6. Store tokens securely
151
+ ```
152
+
153
+ ### Secure Token Storage
154
+
155
+ Your authentication credentials are securely stored using your system's native keychain:
156
+
157
+ - **macOS**: Keychain Access (`boltic-cli` service)
158
+ - **Windows**: Credential Manager (`boltic-cli` service)
159
+ - **Linux**: Secret Service API (`boltic-cli` service)
160
+
161
+ **Stored Credentials:**
162
+
163
+ - `token`: Bearer token for API authentication
164
+ - `session`: Session cookie for web requests
165
+ - `account_id`: Your Boltic account identifier
166
+
167
+ ### Logout and Token Management
168
+
169
+ ```bash
170
+ # Clear all stored credentials
171
+ boltic logout
172
+
173
+ # Check authentication status
174
+ boltic integration list # Will prompt login if not authenticated
175
+ ```
176
+
177
+ ### Troubleshooting Authentication
178
+
179
+ #### Common Issues
180
+
181
+ **Browser doesn't open automatically:**
182
+
183
+ ```bash
184
+ # Manual login URL will be displayed
185
+ # Copy and paste the URL into your browser
186
+ ```
187
+
188
+ **Authentication timeout:**
189
+
190
+ ```bash
191
+ # Retry login (5-minute timeout)
192
+ boltic login
193
+ ```
194
+
195
+ **Connection issues:**
196
+
197
+ ```bash
198
+ # Check network connectivity
199
+ ping console.fynd.com
200
+
201
+ # Try different environment
202
+ boltic env set fcz0
203
+ boltic login
204
+ ```
31
205
 
32
206
  ---
33
207
 
34
208
  ## 🧩 Integration Management
35
209
 
36
- ### Create a New Integration
210
+ ### Creating New Integrations
37
211
 
38
212
  ```bash
39
213
  boltic integration create
40
214
  ```
41
215
 
42
- You’ll be prompted to enter:
216
+ #### Interactive Setup Process
217
+
218
+ The CLI will guide you through:
219
+
220
+ 1. **Integration Details**
221
+ - Name (alphanumeric + underscores only)
222
+ - Description (human-readable)
223
+ - AI-generated description
224
+
225
+ 2. **Visual Assets**
226
+ - Icon selection (SVG format required)
227
+ - Brand colors and styling
228
+
229
+ 3. **Integration Type**
230
+ - **Workflow Activity**: Reusable components for specific tasks
231
+ - **Workflow Trigger**: Event-driven components that initiate workflows
232
+ - **Both**: Create both types simultaneously
233
+
234
+ 4. **Categorization**
235
+ - Integration Group (Analytics, CRM, ERP, Marketing, etc.)
236
+ - Tags and metadata
237
+
238
+ #### Generated Project Structure
239
+
240
+ ```
241
+ my-integration/
242
+ ├── schemas/
243
+ │ ├── resources/
244
+ │ │ └── resource1.json
245
+ │ ├── authentication.json
246
+ │ ├── base.json
247
+ │ └── webhook.json
248
+ ├── Authentication.mdx
249
+ ├── Documentation.mdx
250
+ └── spec.json
251
+ ```
252
+
253
+ **File Descriptions:**
43
254
 
44
- - **Name**: Letters and underscores only (e.g., My_Integration)
45
- - **Icon**: Select an SVG file from your computer
46
- - **Integration Type**:
47
- - Workflow Activity: Reusable components that perform specific tasks
48
- - Workflow Trigger: Components that start your workflow based on external events
49
- - You can choose to create both types for the same integration
255
+ - **`schemas/`** - Schema definitions for the integration
256
+ - **`resources/`** - Resource-specific schemas (e.g., `resource1.json`)
257
+ - **`authentication.json`** - Authentication configuration and parameters
258
+ - **`base.json`** - Base integration configuration and parameters
259
+ - **`webhook.json`** - Webhook configuration (for trigger integrations)
50
260
 
51
- - **Descriptions**
52
- - Human-readable and AI-generated
261
+ - **`Authentication.mdx`** - Authentication documentation in Markdown format
262
+ - **`Documentation.mdx`** - General integration documentation
263
+ - **`spec.json`** - Integration specification and metadata
53
264
 
54
- - **Integration Group**
55
- - e.g., Analytics, CRM, ERP, Marketing, Payment, Social Media, Other
265
+ ### Managing Existing Integrations
56
266
 
57
- ### ✏️ Edit an Integration
267
+ #### Edit Integration
58
268
 
59
269
  ```bash
270
+ # Edit current integration
60
271
  boltic integration edit
272
+
61
273
  ```
62
274
 
63
- ### 🔄 Sync an Integration
275
+ #### Sync Changes
64
276
 
65
277
  ```bash
278
+ # Sync all changes
66
279
  boltic integration sync
67
280
  ```
68
281
 
69
- ### 🚀 Submit an Integration
282
+ #### Pull Latest Changes
70
283
 
71
284
  ```bash
285
+ # Pull latest from Boltic Cloud
286
+ boltic integration pull
287
+ ```
288
+
289
+ #### Submit for Review
290
+
291
+ ```bash
292
+ # Submit for publishing review
72
293
  boltic integration submit
73
294
  ```
74
295
 
75
296
  ---
76
297
 
77
- ## 📌 Command Reference
298
+ ## 📚 Command Reference
78
299
 
79
- | Command | Description |
80
- | --------------------------- | -------------------------------------------------------- |
81
- | `boltic login` | Authenticate with Boltic |
82
- | `boltic integration create` | Create a new integration |
83
- | `boltic integration sync` | Sync changes to your draft |
84
- | `boltic integration submit` | Submit integration for review |
85
- | `boltic integration pull` | Pull the latest changes of an integration from the Cloud |
86
- | `boltic integration edit` | Edit an existing integration |
87
- | `boltic help` | Show CLI help |
88
- | `boltic version` | Display CLI version |
300
+ ### Core Commands
89
301
 
90
- ---
302
+ | Command | Description | Options |
303
+ | ---------------- | --------------------------------- | ------- |
304
+ | `boltic login` | Authenticate with Boltic platform | |
305
+ | `boltic logout` | Clear stored credentials | |
306
+ | `boltic version` | Display CLI version | |
307
+ | `boltic help` | Show comprehensive help | |
91
308
 
92
- ## 🔁 Typical Workflow
309
+ ### Integration Commands
310
+
311
+ | Command | Description | Options |
312
+ | --------------------------- | ------------------------- | --------------------- |
313
+ | `boltic integration create` | Create new integration | Interactive prompts |
314
+ | `boltic integration edit` | Edit existing integration | Interactive prompt |
315
+ | `boltic integration sync` | Sync local changes | Interactive prompt |
316
+ | `boltic integration pull` | Pull latest changes | Interactive prompt |
317
+ | `boltic integration submit` | Submit for review | |
318
+ | `boltic integration status` | Check integration status | Interactive selection |
319
+ | `boltic integration help` | Show integration help | |
320
+
321
+ ### Help and Documentation
93
322
 
94
323
  ```bash
95
- # Step 1: Authenticate
96
- boltic login
324
+ # General help
325
+ boltic help
326
+
327
+ # Command-specific help
328
+ boltic integration help
329
+ boltic login help
330
+ ```
331
+
332
+ ---
333
+
334
+ ## 🛠️ Development Workflow
97
335
 
98
- # Step 2: Start a new integration
336
+ ### Typical Development Cycle
337
+
338
+ ```bash
339
+ # 1. Start Development
99
340
  boltic integration create
341
+ cd my-integration
342
+
343
+ # 2. Make Changes
344
+ # Edit your integration files...
100
345
 
101
- # Step 3: Save changes
346
+ # 3. Sync Changes
102
347
  boltic integration sync
103
348
 
104
- # Step 5: Submit for publishing and review
349
+ # 4. Iterate
350
+ # Make more changes and sync...
351
+
352
+ # 5. Submit for Review
105
353
  boltic integration submit
354
+ ```
106
355
 
107
- # Step 6: Pull the latest changes of a integration. Please call this command inside a integration folder.
108
- boltic integration pull
356
+ ---
109
357
 
110
- # Step 7: Use this command if you don't have folder of a particular integration. Please call this command outside of any existing integration folder.
111
- boltic integration edit
358
+ ## 🐛 Troubleshooting
359
+
360
+ ### Common Issues
361
+
362
+ #### Authentication Problems
363
+
364
+ ```bash
365
+ # Clear stored credentials
366
+ boltic logout
367
+
368
+ # Re-authenticate
369
+ boltic login
370
+
371
+ # Check connection
372
+ ping console.fynd.com
112
373
  ```
113
374
 
114
- ---
375
+ #### Sync Issues
115
376
 
116
- ## 🛠️ Troubleshooting
377
+ ```bash
378
+ # Check status
379
+ boltic integration status
117
380
 
118
- ### Login Errors
381
+ # Force sync
382
+ boltic integration sync
119
383
 
120
- - Make sure you're online and using valid credentials.
121
- - Retry `boltic login` if your token has expired.
384
+ ```
122
385
 
123
- ### Integration Issues
386
+ #### Network Issues
124
387
 
125
- - Ensure all required fields (e.g., name, type, icon URL) are filled.
126
- - Verify the icon URL is publicly accessible.
127
- - Double-check the selected integration type and group.
388
+ ```bash
389
+ # Test connectivity
390
+ ping console.fynd.com
128
391
 
129
- ---
392
+ # Check API status
393
+ boltic integration status
130
394
 
131
- ## 📚 Help
395
+ # Enable verbose logging for debugging
396
+ boltic --verbose integration sync
397
+ ```
398
+
399
+ ### Debug Mode
400
+
401
+ ```bash
402
+ # Enable verbose logging
403
+ boltic --verbose integration sync
404
+
405
+ # Run with verbose output for any command
406
+ boltic --verbose integration create
407
+ boltic --verbose login
408
+ ```
132
409
 
133
- Get help directly in the CLI:
410
+ ### Getting Help
134
411
 
135
412
  ```bash
136
- # View all commands
413
+ # Comprehensive help
137
414
  boltic help
138
415
 
139
- # View integration command options
416
+ # Command-specific help
140
417
  boltic integration help
141
418
  ```
142
419
 
143
- Or visit the [Boltic Docs](https://docs.boltic.io) for full documentation.
420
+ ---
421
+
422
+ ## 📖 Documentation
423
+
424
+ ### Official Resources
425
+
426
+ - 📚 **[Boltic Documentation](https://docs.boltic.io)** - Complete API reference and guides
427
+ - 🎥 **[Video Tutorials](https://boltic.io/tutorials)** - Step-by-step video guides
428
+ - 💡 **[Examples Repository](https://github.com/bolticio/examples)** - Real-world integration examples
429
+ - 🚀 **[Quick Start Guide](https://docs.boltic.io/quickstart)** - Get started in 5 minutes
430
+
431
+ ### Community Resources
432
+
433
+ - 🐛 **[Issue Tracker](https://github.com/bolticio/cli/issues)** - Report bugs and request features
434
+ - 📝 **[Blog](https://boltic.io/blog)** - Latest updates and best practices
435
+
436
+ ### CLI Documentation
437
+
438
+ ```bash
439
+ # Open documentation in browser
440
+ boltic docs
441
+ ```
144
442
 
145
443
  ---
146
444
 
147
- ## 🧾 License
445
+ ## 🤝 Contributing
446
+
447
+ We welcome contributions from the community! Here's how you can help:
448
+
449
+ ### Development Setup
450
+
451
+ ```bash
452
+ # Fork and clone
453
+ git clone https://github.com/your-username/cli.git
454
+ cd cli
455
+
456
+ # Install dependencies
457
+ npm install
458
+
459
+ # Link for development
460
+ npm link
461
+
462
+ # Run tests
463
+ npm test
464
+ ```
465
+
466
+ ### Contribution Guidelines
467
+
468
+ 1. **Fork the repository**
469
+ 2. **Create a feature branch**: `git checkout -b feature/amazing-feature`
470
+ 3. **Make your changes** with proper tests
471
+ 4. **Run the test suite**: `npm test`
472
+ 5. **Commit your changes**: `git commit -m 'Add amazing feature'`
473
+ 6. **Push to the branch**: `git push origin feature/amazing-feature`
474
+ 7. **Open a Pull Request**
475
+
476
+ ### Code Standards
477
+
478
+ - Follow **ESLint** configuration
479
+ - Write **comprehensive tests** for new features
480
+ - Update **documentation** for API changes
481
+ - Follow **conventional commits** for commit messages
482
+
483
+ ---
484
+
485
+ ## 📄 License
486
+
487
+ This project is licensed under the **ISC License** - see the [LICENSE](LICENSE) file for details.
488
+
489
+ ---
490
+
491
+ ## 🙏 Acknowledgments
492
+
493
+ - **Boltic Team** - For building an amazing platform
494
+ - **Open Source Contributors** - For their valuable contributions
495
+ - **Community** - For feedback, bug reports, and feature requests
496
+
497
+ ---
498
+
499
+ <div align="center">
500
+
501
+ **Made with ❤️ by the Boltic Team**
502
+
503
+ [Website](https://boltic.io) • [Documentation](https://docs.boltic.io) • [Support](https://support.boltic.io) • [Discord](https://discord.gg/boltic)
148
504
 
149
- MIT © [Boltic](https://boltic.io)
505
+ </div>
@@ -55,7 +55,7 @@ const commands = {
55
55
  action: handleStatus,
56
56
  },
57
57
  test: {
58
- description: "Run tests for the integration",
58
+ description: "Run tests for the integration with coverage",
59
59
  action: handleTest,
60
60
  },
61
61
  help: {
@@ -927,6 +927,26 @@ function showHelp() {
927
927
  Object.entries(commands).forEach(([cmd, details]) => {
928
928
  console.log(chalk.bold(`${cmd}`) + ` - ${details.description}`);
929
929
  });
930
+
931
+ console.log(chalk.cyan("\nTest Command Usage:"));
932
+ console.log(chalk.dim(" boltic integration test"));
933
+ console.log(chalk.dim(" boltic integration test --path /path/to/project"));
934
+ console.log(chalk.dim(" boltic integration test --config jest.config.js"));
935
+ console.log(chalk.dim(" boltic integration test --watchAll"));
936
+ console.log(chalk.dim(" boltic integration test --updateSnapshot"));
937
+
938
+ console.log(chalk.cyan("\nSupported Flags:"));
939
+ console.log(
940
+ chalk.dim(" --path <directory> Run tests in specific directory")
941
+ );
942
+ console.log(
943
+ chalk.dim(" --config <file> Use specific Jest config file")
944
+ );
945
+ console.log(
946
+ chalk.dim(" --watchAll Watch all files for changes")
947
+ );
948
+ console.log(chalk.dim(" --updateSnapshot Update snapshots"));
949
+ console.log(chalk.dim(" All other Jest CLI flags are supported"));
930
950
  }
931
951
 
932
952
  // Show detailed information about an integration
@@ -1054,7 +1074,9 @@ async function handleStatus() {
1054
1074
  async function handleTest(args) {
1055
1075
  // Parse command line arguments
1056
1076
  let currentDir = process.cwd();
1077
+ let jestConfigFile = null;
1057
1078
  const pathIndex = args.indexOf("--path");
1079
+ const configIndex = args.indexOf("--config");
1058
1080
 
1059
1081
  if (pathIndex !== -1 && args[pathIndex + 1]) {
1060
1082
  currentDir = args[pathIndex + 1];
@@ -1069,72 +1091,228 @@ async function handleTest(args) {
1069
1091
  }
1070
1092
  }
1071
1093
 
1094
+ if (configIndex !== -1 && args[configIndex + 1]) {
1095
+ jestConfigFile = args[configIndex + 1];
1096
+ // Validate the config file path
1097
+ const configPath = path.isAbsolute(jestConfigFile)
1098
+ ? jestConfigFile
1099
+ : path.join(currentDir, jestConfigFile);
1100
+
1101
+ if (!fs.existsSync(configPath)) {
1102
+ console.error(
1103
+ chalk.red(
1104
+ `Error: Jest config file does not exist: ${configPath}`
1105
+ )
1106
+ );
1107
+ return;
1108
+ }
1109
+ }
1110
+
1072
1111
  const { spawn } = await import("child_process");
1073
1112
 
1074
1113
  console.log(chalk.cyan.bold("\n🧪 Running integration tests...\n"));
1075
1114
 
1076
- // Look for test directory
1115
+ // Look for test directory and test files
1077
1116
  const testDirs = ["test", "tests", "__tests__"];
1078
1117
  let testDir = null;
1118
+ let testFiles = [];
1079
1119
 
1120
+ // Check for test directories
1080
1121
  for (const dir of testDirs) {
1081
- if (fs.existsSync(dir)) {
1122
+ const testPath = path.join(currentDir, dir);
1123
+ if (fs.existsSync(testPath)) {
1082
1124
  testDir = dir;
1125
+ // Find all test files in the directory
1126
+ const files = fs.readdirSync(testPath);
1127
+ testFiles = files.filter(
1128
+ (file) =>
1129
+ file.endsWith(".test.js") ||
1130
+ file.endsWith(".spec.js") ||
1131
+ file.endsWith(".test.ts") ||
1132
+ file.endsWith(".spec.ts")
1133
+ );
1083
1134
  break;
1084
1135
  }
1085
1136
  }
1086
1137
 
1138
+ // If no test directory found, look for test files in current directory
1087
1139
  if (!testDir) {
1140
+ const currentDirFiles = fs.readdirSync(currentDir);
1141
+ testFiles = currentDirFiles.filter(
1142
+ (file) =>
1143
+ file.endsWith(".test.js") ||
1144
+ file.endsWith(".spec.js") ||
1145
+ file.endsWith(".test.ts") ||
1146
+ file.endsWith(".spec.ts")
1147
+ );
1148
+
1149
+ if (testFiles.length === 0) {
1150
+ console.log(
1151
+ chalk.yellow(
1152
+ "⚠️ No test files found. Looked for: test, tests, __tests__ directories and *.test.js, *.spec.js, *.test.ts, *.spec.ts files"
1153
+ )
1154
+ );
1155
+ return;
1156
+ }
1157
+ }
1158
+
1159
+ if (testFiles.length === 0 && testDir) {
1088
1160
  console.log(
1089
1161
  chalk.yellow(
1090
- "⚠️ No test directory found. Looked for: test, tests, __tests__"
1162
+ `⚠️ No test files found in ${testDir} directory. Looking for: *.test.js, *.spec.js, *.test.ts, *.spec.ts files`
1091
1163
  )
1092
1164
  );
1093
1165
  return;
1094
1166
  }
1095
1167
 
1096
- console.log(chalk.dim(`📁 Found test directory: ${testDir}`));
1168
+ console.log(chalk.dim(`📁 Found test directory: ${testDir || currentDir}`));
1169
+ console.log(
1170
+ chalk.dim(
1171
+ `📋 Found ${testFiles.length} test file(s): ${testFiles.join(", ")}`
1172
+ )
1173
+ );
1097
1174
 
1098
- // Check if Jest is available
1099
- const packageJsonPath = path.join(process.cwd(), "package.json");
1100
- let hasJest = false;
1175
+ // Check for Jest configuration
1176
+ let jestConfigPath;
1177
+ if (jestConfigFile) {
1178
+ jestConfigPath = path.isAbsolute(jestConfigFile)
1179
+ ? jestConfigFile
1180
+ : path.join(currentDir, jestConfigFile);
1181
+ } else {
1182
+ // Look for default config files
1183
+ const defaultConfigs = [
1184
+ "jest.config.cjs",
1185
+ "jest.config.js",
1186
+ "jest.config.json",
1187
+ ];
1188
+ for (const configFile of defaultConfigs) {
1189
+ const configPath = path.join(currentDir, configFile);
1190
+ if (fs.existsSync(configPath)) {
1191
+ jestConfigPath = configPath;
1192
+ break;
1193
+ }
1194
+ }
1195
+ }
1101
1196
 
1102
- if (fs.existsSync(packageJsonPath)) {
1103
- try {
1104
- const packageJson = JSON.parse(
1105
- fs.readFileSync(packageJsonPath, "utf-8")
1106
- );
1107
- hasJest = !!(
1108
- packageJson.devDependencies?.jest ||
1109
- packageJson.dependencies?.jest
1197
+ const jestConfigExists = !!jestConfigPath;
1198
+ const configFileName = jestConfigPath
1199
+ ? path.basename(jestConfigPath)
1200
+ : null;
1201
+
1202
+ console.log(
1203
+ chalk.dim(
1204
+ `⚙️ Jest configuration: ${jestConfigExists ? `Found ${configFileName}` : "Using default configuration"}`
1205
+ )
1206
+ );
1207
+
1208
+ // Prepare Jest arguments
1209
+ const jestArgs = ["jest"];
1210
+
1211
+ // Add basic Jest flags
1212
+ jestArgs.push("--coverage");
1213
+ jestArgs.push("--verbose");
1214
+ jestArgs.push("--colors");
1215
+ jestArgs.push("--passWithNoTests");
1216
+
1217
+ // Add config file if exists, otherwise use simple patterns
1218
+ if (jestConfigExists) {
1219
+ jestArgs.push("--config", jestConfigPath);
1220
+ } else {
1221
+ // Use simple test matching patterns
1222
+ if (testDir) {
1223
+ jestArgs.push(
1224
+ "--testMatch",
1225
+ `**/${testDir}/**/*.{test,spec}.{js,ts}`
1110
1226
  );
1111
- } catch (error) {
1112
- console.log(chalk.yellow("⚠️ Could not read package.json"));
1227
+ } else {
1228
+ jestArgs.push("--testMatch", "**/*.{test,spec}.{js,ts}");
1113
1229
  }
1230
+ jestArgs.push("--collectCoverageFrom", "lib/**/*.{js,ts}");
1231
+ jestArgs.push("--collectCoverageFrom", "src/**/*.{js,ts}");
1232
+ jestArgs.push("--collectCoverageFrom", "*.{js,ts}");
1233
+ jestArgs.push("--coveragePathIgnorePatterns", "/node_modules/");
1234
+ jestArgs.push("--coveragePathIgnorePatterns", "/tests/");
1114
1235
  }
1115
1236
 
1116
- if (!hasJest) {
1237
+ // Add any additional Jest arguments passed by user
1238
+ const additionalArgs = args.filter((arg, index) => {
1239
+ // Skip our custom flags and their values
1240
+ if (arg === "--path" || arg === "--config") return false;
1241
+ if (args[index - 1] === "--path" || args[index - 1] === "--config")
1242
+ return false;
1243
+ return true;
1244
+ });
1245
+
1246
+ if (additionalArgs.length > 0) {
1247
+ jestArgs.push(...additionalArgs);
1117
1248
  console.log(
1118
- chalk.red(
1119
- "❌ Jest is not installed. Please install Jest to run tests."
1249
+ chalk.dim(
1250
+ `🔧 Additional Jest arguments: ${additionalArgs.join(" ")}`
1120
1251
  )
1121
1252
  );
1122
- return;
1123
1253
  }
1124
1254
 
1125
- // Run Jest with the test directory
1255
+ console.log(chalk.dim(`🚀 Running command: npx ${jestArgs.join(" ")}`));
1256
+ console.log(chalk.cyan("─".repeat(50)));
1257
+
1258
+ // Run Jest with coverage
1126
1259
  return new Promise((resolve, reject) => {
1127
- const jestProcess = spawn("npx", ["jest", testDir, "--verbose"], {
1260
+ const jestProcess = spawn("npx", jestArgs, {
1128
1261
  stdio: "inherit",
1129
1262
  shell: true,
1263
+ cwd: currentDir,
1130
1264
  });
1131
1265
 
1132
1266
  jestProcess.on("close", (code) => {
1267
+ console.log(chalk.cyan("─".repeat(50)));
1268
+
1133
1269
  if (code === 0) {
1134
1270
  console.log(chalk.green.bold("\n✅ All tests passed!"));
1271
+
1272
+ // Check for coverage directory
1273
+ const coverageDir = path.join(currentDir, "coverage");
1274
+ if (fs.existsSync(coverageDir)) {
1275
+ console.log(chalk.cyan("\n📊 Coverage Report Generated:"));
1276
+ console.log(chalk.dim(` Directory: ${coverageDir}`));
1277
+
1278
+ // Check for HTML report
1279
+ const htmlReportPath = path.join(
1280
+ coverageDir,
1281
+ "lcov-report",
1282
+ "index.html"
1283
+ );
1284
+ if (fs.existsSync(htmlReportPath)) {
1285
+ console.log(
1286
+ chalk.dim(` HTML Report: ${htmlReportPath}`)
1287
+ );
1288
+ }
1289
+
1290
+ // Check for text report
1291
+ const textReportPath = path.join(coverageDir, "lcov.info");
1292
+ if (fs.existsSync(textReportPath)) {
1293
+ console.log(
1294
+ chalk.dim(` LCOV Report: ${textReportPath}`)
1295
+ );
1296
+ }
1297
+ }
1298
+
1299
+ // Show test summary
1300
+ console.log(chalk.cyan("\n📋 Test Summary:"));
1301
+ console.log(chalk.dim(` Test files: ${testFiles.length}`));
1302
+ console.log(
1303
+ chalk.dim(` Test directory: ${testDir || currentDir}`)
1304
+ );
1305
+ console.log(chalk.green(" Status: PASSED"));
1135
1306
  } else {
1136
1307
  console.log(chalk.red.bold("\n❌ Some tests failed."));
1308
+ console.log(chalk.red(` Exit code: ${code}`));
1137
1309
  }
1310
+
1311
+ console.log(
1312
+ chalk.cyan(
1313
+ "\n💡 Tip: Check the coverage directory for detailed coverage reports"
1314
+ )
1315
+ );
1138
1316
  resolve(code);
1139
1317
  });
1140
1318
 
@@ -52,8 +52,14 @@ const findResourceFieldsWithOptions = (schema, fileLabel, errors) => {
52
52
  }
53
53
  return resourceFields;
54
54
  };
55
- const findOperationFieldsWithOptions = (schema, fileLabel, errors) => {
55
+ const findOperationFieldsWithOptions = (
56
+ schema,
57
+ fileLabel,
58
+ errors,
59
+ expectedResourceName
60
+ ) => {
56
61
  const operationFields = [];
62
+
57
63
  if (Array.isArray(schema?.parameters)) {
58
64
  schema.parameters.forEach((param) => {
59
65
  if (
@@ -66,12 +72,28 @@ const findOperationFieldsWithOptions = (schema, fileLabel, errors) => {
66
72
  fileLabel,
67
73
  errors
68
74
  );
69
- operationFields.push(
70
- ...param.meta.options.map((opt) => opt.value)
71
- );
75
+ param.meta.options.forEach((opt, index) => {
76
+ if (typeof opt.value === "string") {
77
+ const parts = opt.value.split(".");
78
+ if (parts.length < 2) {
79
+ errors.add(
80
+ `"operation" field in "${fileLabel}" has an invalid option at index ${index} with value "${opt.value}". Expected format "resource.operation".`
81
+ );
82
+ } else {
83
+ const resource = parts[0];
84
+ if (resource !== expectedResourceName) {
85
+ errors.add(
86
+ `"operation" field in "${fileLabel}" has an inconsistent resource prefix at index ${index}. Found "${resource}" but expected "${expectedResourceName}".`
87
+ );
88
+ }
89
+ operationFields.push(opt.value);
90
+ }
91
+ }
92
+ });
72
93
  }
73
94
  });
74
95
  }
96
+
75
97
  return operationFields;
76
98
  };
77
99
 
@@ -228,6 +250,23 @@ const validateComponentSchemas = (schemas, errors) => {
228
250
  errors.add(`"${schema.name}" is missing a description.`);
229
251
  }
230
252
 
253
+ // 🚨 Validate for duplicate options with same label and value
254
+ if (Array.isArray(schema.meta.options)) {
255
+ const seen = new Set();
256
+ schema.meta.options.forEach((option, index) => {
257
+ if (option && typeof option === "object") {
258
+ const key = `${option.label}::${option.value}`;
259
+ if (seen.has(key)) {
260
+ errors.add(
261
+ `"${schema.name}" contains duplicate option at index ${index} with label "${option.label}" and value "${option.value}".`
262
+ );
263
+ } else {
264
+ seen.add(key);
265
+ }
266
+ }
267
+ });
268
+ }
269
+
231
270
  // Validate against the specific component type schema
232
271
  validateComponentByType(schema, schema.meta.displayType, errors);
233
272
  });
@@ -366,7 +405,8 @@ const validateResources = (resourcesDir, resourceFields, errors) => {
366
405
  const operationFields = findOperationFieldsWithOptions(
367
406
  schema,
368
407
  `${resourceFile}.json`,
369
- errors
408
+ errors,
409
+ resourceFile
370
410
  );
371
411
 
372
412
  operationFields.forEach((operation) => {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@boltic/cli",
3
- "version": "1.0.6-beta.12",
3
+ "version": "1.0.6-beta.14",
4
4
  "description": "A powerful CLI tool for managing Boltic Workflow integrations",
5
5
  "main": "index.js",
6
6
  "bin": {
@@ -44,7 +44,8 @@
44
44
  "keytar": "^7.9.0",
45
45
  "open": "^10.1.0",
46
46
  "uuid": "^11.1.0",
47
- "lodash.isempty": "^4.4.0"
47
+ "lodash.isempty": "^4.4.0",
48
+ "jest": "^29.7.0"
48
49
  },
49
50
  "devDependencies": {
50
51
  "@babel/core": "^7.26.9",
@@ -58,7 +59,6 @@
58
59
  "eslint-config-prettier": "^10.0.2",
59
60
  "eslint-plugin-prettier": "^5.2.3",
60
61
  "husky": "^9.1.7",
61
- "jest": "^29.7.0",
62
62
  "lint-staged": "^15.4.3",
63
63
  "nodemon": "^3.1.9",
64
64
  "prettier": "^3.5.3"
@@ -51,12 +51,22 @@ const base = (name, create_catalogue) => {
51
51
  name: "secret",
52
52
  meta: {
53
53
  displayName: "Service Account",
54
- displayType: "hidden",
54
+ displayType: "autocomplete",
55
55
  placeholder: "Select Service Account",
56
56
  description:
57
57
  "Your service account credentials are encrypted & can be removed at any time.",
58
58
  options: [],
59
- value: `__BOLTIC_INTEGRATION_${name.toUpperCase()}`,
59
+ config: {
60
+ urlType: "secret",
61
+ method: "get",
62
+ url: `/${name.toUpperCase()}?current_page=1&page_size=999`,
63
+ labelKey: "name",
64
+ valueKey: "_id",
65
+ },
66
+ htmlProps: {
67
+ showAddNew: true,
68
+ },
69
+ value: "",
60
70
  validation: {
61
71
  required: true,
62
72
  },
@@ -66,22 +76,12 @@ const base = (name, create_catalogue) => {
66
76
  name: "secret",
67
77
  meta: {
68
78
  displayName: "Service Account",
69
- displayType: "autocomplete",
79
+ displayType: "hidden",
70
80
  placeholder: "Select Service Account",
71
81
  description:
72
82
  "Your service account credentials are encrypted & can be removed at any time.",
73
83
  options: [],
74
- config: {
75
- urlType: "secret",
76
- method: "get",
77
- url: `/${name.toUpperCase()}?current_page=1&page_size=999`,
78
- labelKey: "name",
79
- valueKey: "_id",
80
- },
81
- htmlProps: {
82
- showAddNew: true,
83
- },
84
- value: "",
84
+ value: `__BOLTIC_INTEGRATION_${name.toUpperCase()}`,
85
85
  validation: {
86
86
  required: true,
87
87
  },