Skip to content

Commit e114768

Browse files
authored
Merge pull request #33 from burgan-tech/master
v1.0.7
2 parents 439c5b4 + 8cddb03 commit e114768

12 files changed

Lines changed: 337 additions & 206 deletions

File tree

README.md

Lines changed: 47 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -24,10 +24,11 @@ npm install -g @burgan-tech/vnext-workflow-cli
2424
npm install @burgan-tech/vnext-workflow-cli
2525
```
2626

27-
After installation, you can use the CLI with:
27+
After installation, you can use the CLI with any of these aliases:
2828
```bash
29-
wf --version
30-
wf check
29+
wf --version # short alias
30+
vnext --version # alternative alias (recommended for Windows)
31+
workflow --version # full name
3132
```
3233

3334
### Install from Source
@@ -118,6 +119,8 @@ wf check
118119

119120
**Note:** The CLI automatically uses the current working directory as the project root. Just `cd` into your project folder before running commands.
120121

122+
> **Tip:** All examples use `wf` but you can also use `vnext` or `workflow` interchangeably. The `vnext` alias is recommended on Windows where `wf` may conflict with existing system commands.
123+
121124
### Basic Usage
122125

123126
```bash
@@ -420,18 +423,20 @@ wf csx
420423

421424
### 6. Multidomain Workflow
422425
```bash
423-
# Add domains
426+
# Add domains (one-time setup)
424427
wf domain add domain-a --API_BASE_URL http://localhost:4201 --DB_NAME vNext_DomainA
425428
wf domain add domain-b --API_BASE_URL http://localhost:4221 --DB_NAME vNext_DomainB
426429

427-
# Work on Domain A
428-
wf domain use domain-a
429-
wf check
430-
wf update
430+
# Option A: Auto-switch via vnext.config.json (recommended)
431+
# Just cd into the project - domain profile switches automatically
432+
cd ~/projects/domain-a-app # vnext.config.json has "domain": "domain-a"
433+
wf update # auto-switches to domain-a profile
431434

432-
# Switch to Domain B - config is applied automatically
433-
wf domain use domain-b
434-
wf check
435+
cd ~/projects/domain-b-app # vnext.config.json has "domain": "domain-b"
436+
wf update # auto-switches to domain-b profile
437+
438+
# Option B: Manual switch (still works)
439+
wf domain use domain-a
435440
wf update
436441

437442
# See all domains
@@ -457,6 +462,35 @@ wf domain list
457462

458463
The CLI supports managing multiple domain configurations. Each domain has its own `API_BASE_URL`, `DB_NAME`, and other settings. Switch between domains with a single command.
459464

465+
### Auto Domain Resolution
466+
467+
When you run any command inside a vNext workspace that contains a `vnext.config.json`, the CLI **automatically** switches to the matching domain profile based on the `domain` field in the config file. This eliminates the need to manually run `wf domain use <name>` every time you switch between projects.
468+
469+
**How it works:**
470+
1. Before each command (except `wf domain`), the CLI checks if `vnext.config.json` exists in the current directory.
471+
2. If found, it reads the `domain` field and looks for a matching CLI domain profile (`DOMAINS[].DOMAIN_NAME`).
472+
3. If a match is found and it differs from the current active domain, it silently switches and shows a dim log message:
473+
```
474+
[auto] Domain switched to "onboarding" (from vnext.config.json)
475+
```
476+
4. If no `vnext.config.json` is found or no matching profile exists, the current active domain is kept (no error).
477+
478+
**Example:** You have two projects and two domain profiles:
479+
```bash
480+
# Add domain profiles once
481+
wf domain add core --DB_NAME vNext_Core
482+
wf domain add onboarding --DB_NAME vNext_Onboarding
483+
484+
# Now just cd into the project and run commands - domain switches automatically
485+
cd ~/projects/core-app # has vnext.config.json with "domain": "core"
486+
wf update # auto-switches to "core" profile
487+
488+
cd ~/projects/onboarding-app # has vnext.config.json with "domain": "onboarding"
489+
wf update # auto-switches to "onboarding" profile
490+
```
491+
492+
> **Note:** The `wf domain` command is excluded from auto-resolution so that manual domain management is never interfered with.
493+
460494
### Backward Compatibility
461495

462496
- Existing single-domain configurations are automatically migrated to the new format.
@@ -583,8 +617,9 @@ wf config get DOCKER_POSTGRES_CONTAINER
583617

584618
### "npm link not working"
585619
```bash
586-
# Use alias
620+
# Use alias (wf or vnext)
587621
echo 'alias wf="node $(pwd)/bin/workflow.js"' >> ~/.bashrc
622+
echo 'alias vnext="node $(pwd)/bin/workflow.js"' >> ~/.bashrc
588623
source ~/.bashrc
589624
```
590625

bin/workflow.js

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,10 @@ const { program, Argument } = require('commander');
44
const chalk = require('chalk');
55
const pkg = require('../package.json');
66

7+
// Config
8+
const config = require('../src/lib/config');
9+
const { printActiveDomainBanner } = require('../src/lib/ui');
10+
711
// Commands
812
const checkCommand = require('../src/commands/check');
913
const csxCommand = require('../src/commands/csx');
@@ -18,6 +22,18 @@ program
1822
.description('vNext Workflow Manager CLI')
1923
.version(pkg.version);
2024

25+
// Auto-resolve domain and show banner before each command
26+
program.hook('preAction', (thisCommand, actionCommand) => {
27+
if (actionCommand.name() === 'domain') return;
28+
29+
const result = config.resolveWorkspaceDomain(process.cwd());
30+
if (result.resolved && result.switched) {
31+
console.log(chalk.dim(` [auto] Domain switched to "${result.domain}" (from vnext.config.json)`));
32+
}
33+
34+
printActiveDomainBanner();
35+
});
36+
2137
// Check command
2238
program
2339
.command('check')

package-lock.json

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,8 @@
55
"main": "dist/index.js",
66
"bin": {
77
"workflow": "./bin/workflow.js",
8-
"wf": "./bin/workflow.js"
8+
"wf": "./bin/workflow.js",
9+
"vnext": "./bin/workflow.js"
910
},
1011
"scripts": {
1112
"dev": "node bin/workflow.js",

src/commands/check.js

Lines changed: 1 addition & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -5,22 +5,7 @@ const { discoverComponents, listDiscovered } = require('../lib/discover');
55
const { getDomain, getComponentTypes, getComponentsRoot } = require('../lib/vnextConfig');
66
const { testApiConnection } = require('../lib/api');
77
const { testDbConnection } = require('../lib/db');
8-
9-
// Logging helpers
10-
const LOG = {
11-
separator: () => console.log(chalk.cyan('═'.repeat(60))),
12-
subSeparator: () => console.log(chalk.cyan('─'.repeat(60))),
13-
header: (text) => {
14-
console.log();
15-
LOG.separator();
16-
console.log(chalk.cyan.bold(` ${text}`));
17-
LOG.separator();
18-
},
19-
success: (text) => console.log(chalk.green(` ✓ ${text}`)),
20-
error: (text) => console.log(chalk.red(` ✗ ${text}`)),
21-
warning: (text) => console.log(chalk.yellow(` ⚠ ${text}`)),
22-
info: (text) => console.log(chalk.dim(` ○ ${text}`))
23-
};
8+
const { LOG } = require('../lib/ui');
249

2510
async function checkCommand() {
2611
LOG.header('SYSTEM CHECK');

src/commands/csx.js

Lines changed: 2 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -4,34 +4,7 @@ const path = require('path');
44
const config = require('../lib/config');
55
const { getDomain } = require('../lib/vnextConfig');
66
const { processCsxFile, getGitChangedCsx, findAllCsx } = require('../lib/csx');
7-
8-
// Logging helpers
9-
const LOG = {
10-
separator: () => console.log(chalk.cyan('═'.repeat(60))),
11-
subSeparator: () => console.log(chalk.cyan('─'.repeat(60))),
12-
header: (text) => {
13-
console.log();
14-
LOG.separator();
15-
console.log(chalk.cyan.bold(` ${text}`));
16-
LOG.separator();
17-
},
18-
success: (text) => console.log(chalk.green(` ✓ ${text}`)),
19-
error: (text) => console.log(chalk.red(` ✗ ${text}`)),
20-
warning: (text) => console.log(chalk.yellow(` ⚠ ${text}`)),
21-
info: (text) => console.log(chalk.dim(` ○ ${text}`)),
22-
component: (type, name, status, detail = '') => {
23-
const typeLabel = chalk.cyan(`[${type}]`);
24-
const nameLabel = chalk.white(name);
25-
if (status === 'success') {
26-
console.log(` ${typeLabel} ${chalk.green('✓')} ${nameLabel} ${chalk.dim(detail)}`);
27-
} else if (status === 'error') {
28-
console.log(` ${typeLabel} ${chalk.red('✗')} ${nameLabel}`);
29-
if (detail) console.log(chalk.red(` └─ ${detail}`));
30-
} else if (status === 'skip') {
31-
console.log(` ${typeLabel} ${chalk.dim('○')} ${nameLabel} ${chalk.dim(detail)}`);
32-
}
33-
}
34-
};
7+
const { LOG } = require('../lib/ui');
358

369
async function csxCommand(options) {
3710
LOG.header('CSX UPDATE');
@@ -40,9 +13,7 @@ async function csxCommand(options) {
4013

4114
// Check domain
4215
try {
43-
const domain = getDomain(projectRoot);
44-
console.log(chalk.dim(` Domain: ${domain}`));
45-
console.log();
16+
getDomain(projectRoot);
4617
} catch (error) {
4718
LOG.error(`Failed to read vnext.config.json: ${error.message}`);
4819
return;

src/commands/reset.js

Lines changed: 4 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -9,34 +9,7 @@ const { getDomain, getComponentTypes } = require('../lib/vnextConfig');
99
const { getJsonMetadata, findAllJson, detectComponentType } = require('../lib/workflow');
1010
const { publishComponent, reinitializeSystem } = require('../lib/api');
1111
const { getInstanceId, deleteWorkflow } = require('../lib/db');
12-
13-
// Logging helpers
14-
const LOG = {
15-
separator: () => console.log(chalk.cyan('═'.repeat(60))),
16-
subSeparator: () => console.log(chalk.cyan('─'.repeat(60))),
17-
header: (text) => {
18-
console.log();
19-
LOG.separator();
20-
console.log(chalk.cyan.bold(` ${text}`));
21-
LOG.separator();
22-
},
23-
success: (text) => console.log(chalk.green(` ✓ ${text}`)),
24-
error: (text) => console.log(chalk.red(` ✗ ${text}`)),
25-
warning: (text) => console.log(chalk.yellow(` ⚠ ${text}`)),
26-
info: (text) => console.log(chalk.dim(` ○ ${text}`)),
27-
component: (type, name, status, detail = '') => {
28-
const typeLabel = chalk.cyan(`[${type}]`);
29-
const nameLabel = chalk.white(name);
30-
if (status === 'success') {
31-
console.log(` ${typeLabel} ${chalk.green('✓')} ${nameLabel} ${chalk.dim(detail)}`);
32-
} else if (status === 'error') {
33-
console.log(` ${typeLabel} ${chalk.red('✗')} ${nameLabel}`);
34-
if (detail) console.log(chalk.red(` └─ ${detail}`));
35-
} else if (status === 'skip') {
36-
console.log(` ${typeLabel} ${chalk.dim('○')} ${nameLabel} ${chalk.dim(detail)}`);
37-
}
38-
}
39-
};
12+
const { LOG, printApiError, printErrorSummaryTable } = require('../lib/ui');
4013

4114
async function resetCommand(options) {
4215
LOG.header('COMPONENT RESET (Force Update)');
@@ -72,10 +45,6 @@ async function resetCommand(options) {
7245
domain: domain
7346
};
7447

75-
console.log(chalk.dim(` Domain: ${domain}`));
76-
console.log(chalk.dim(` API: ${apiConfig.baseUrl}`));
77-
console.log();
78-
7948
// Discover folders
8049
const spinner = ora(' Scanning folders...').start();
8150
let discovered;
@@ -210,9 +179,9 @@ async function resetCommand(options) {
210179
LOG.component(type, fileName, 'success', `→ ${action}`);
211180
componentStats[type].success++;
212181
} else {
213-
LOG.component(type, fileName, 'error', result.error);
182+
printApiError(result, type, fileName);
214183
componentStats[type].failed++;
215-
errors.push({ type, file: fileName, error: result.error });
184+
errors.push({ type, file: fileName, error: result.error, statusCode: result.statusCode, apiError: result.apiError });
216185
}
217186
} catch (error) {
218187
const errorMsg = error.message || 'Unknown error';
@@ -257,12 +226,7 @@ async function resetCommand(options) {
257226
if (errors.length > 0) {
258227
console.log();
259228
LOG.subSeparator();
260-
console.log(chalk.red.bold('\n ERRORS:\n'));
261-
262-
for (const err of errors) {
263-
console.log(chalk.red(` [${err.type}] ${err.file}`));
264-
console.log(chalk.dim(` └─ ${err.error}`));
265-
}
229+
printErrorSummaryTable(errors);
266230
}
267231

268232
LOG.separator();

src/commands/sync.js

Lines changed: 9 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -8,34 +8,7 @@ const { publishComponent, reinitializeSystem } = require('../lib/api');
88
const { getInstanceId, deleteWorkflow } = require('../lib/db');
99
const { getJsonMetadata, detectComponentType } = require('../lib/workflow');
1010
const { processCsxFile, findAllCsx } = require('../lib/csx');
11-
12-
// Logging helpers
13-
const LOG = {
14-
separator: () => console.log(chalk.cyan('═'.repeat(60))),
15-
subSeparator: () => console.log(chalk.cyan('─'.repeat(60))),
16-
header: (text) => {
17-
console.log();
18-
LOG.separator();
19-
console.log(chalk.cyan.bold(` ${text}`));
20-
LOG.separator();
21-
},
22-
success: (text) => console.log(chalk.green(` ✓ ${text}`)),
23-
error: (text) => console.log(chalk.red(` ✗ ${text}`)),
24-
warning: (text) => console.log(chalk.yellow(` ⚠ ${text}`)),
25-
info: (text) => console.log(chalk.dim(` ○ ${text}`)),
26-
component: (type, name, status, detail = '') => {
27-
const typeLabel = chalk.cyan(`[${type}]`);
28-
const nameLabel = chalk.white(name);
29-
if (status === 'success') {
30-
console.log(` ${typeLabel} ${chalk.green('✓')} ${nameLabel} ${chalk.dim(detail)}`);
31-
} else if (status === 'error') {
32-
console.log(` ${typeLabel} ${chalk.red('✗')} ${nameLabel}`);
33-
if (detail) console.log(chalk.red(` └─ ${detail}`));
34-
} else if (status === 'skip') {
35-
console.log(` ${typeLabel} ${chalk.dim('○')} ${nameLabel} ${chalk.dim(detail)}`);
36-
}
37-
}
38-
};
11+
const { LOG, printApiError, printErrorSummaryTable } = require('../lib/ui');
3912

4013
async function syncCommand() {
4114
LOG.header('SYSTEM SYNC - Add Missing Components');
@@ -77,10 +50,6 @@ async function syncCommand() {
7750
domain: domain
7851
};
7952

80-
console.log(chalk.dim(` Domain: ${domain}`));
81-
console.log(chalk.dim(` API: ${apiConfig.baseUrl}`));
82-
console.log();
83-
8453
// Discover folders
8554
const discoverSpinner = ora('Scanning folders...').start();
8655
let discovered;
@@ -183,9 +152,9 @@ async function syncCommand() {
183152
LOG.component(type, fileName, 'success', '→ published');
184153
componentStats[type].success++;
185154
} else {
186-
LOG.component(type, fileName, 'error', result.error);
155+
printApiError(result, type, fileName);
187156
componentStats[type].failed++;
188-
errors.push({ type, file: fileName, error: result.error });
157+
errors.push({ type, file: fileName, error: result.error, statusCode: result.statusCode, apiError: result.apiError });
189158
}
190159
} catch (error) {
191160
const errorMsg = error.message || 'Unknown error';
@@ -239,20 +208,14 @@ async function syncCommand() {
239208
}
240209

241210
// Errors
242-
if (errors.length > 0 || csxResults.errors.length > 0) {
211+
const allErrors = [
212+
...errors,
213+
...csxResults.errors.map(e => ({ type: 'CSX', file: e.file, error: e.error }))
214+
];
215+
if (allErrors.length > 0) {
243216
console.log();
244217
LOG.subSeparator();
245-
console.log(chalk.red.bold('\n ERRORS:\n'));
246-
247-
for (const err of errors) {
248-
console.log(chalk.red(` [${err.type}] ${err.file}`));
249-
console.log(chalk.dim(` └─ ${err.error}`));
250-
}
251-
252-
for (const err of csxResults.errors) {
253-
console.log(chalk.red(` [CSX] ${err.file}`));
254-
console.log(chalk.dim(` └─ ${err.error}`));
255-
}
218+
printErrorSummaryTable(allErrors);
256219
}
257220

258221
LOG.separator();

0 commit comments

Comments
 (0)