@burgan-tech/vnext-workflow-cli 1.0.1 → 1.0.2
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 +244 -82
- package/bin/workflow.js +0 -0
- package/package.json +5 -5
- package/src/commands/check.js +62 -22
- package/src/commands/config.js +5 -6
- package/src/commands/csx.js +125 -44
- package/src/commands/reset.js +198 -80
- package/src/commands/sync.js +189 -107
- package/src/commands/update.js +217 -99
- package/src/lib/api.js +52 -34
- package/src/lib/config.js +43 -5
- package/src/lib/csx.js +130 -57
- package/src/lib/discover.js +131 -29
- package/src/lib/vnextConfig.js +124 -0
- package/src/lib/workflow.js +86 -39
package/src/commands/sync.js
CHANGED
|
@@ -2,187 +2,269 @@ const chalk = require('chalk');
|
|
|
2
2
|
const ora = require('ora');
|
|
3
3
|
const path = require('path');
|
|
4
4
|
const config = require('../lib/config');
|
|
5
|
-
const { discoverComponents } = require('../lib/discover');
|
|
6
|
-
const {
|
|
7
|
-
const {
|
|
8
|
-
const {
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
const {
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
5
|
+
const { discoverComponents, findAllJsonFiles } = require('../lib/discover');
|
|
6
|
+
const { getDomain, getComponentTypes } = require('../lib/vnextConfig');
|
|
7
|
+
const { publishComponent, reinitializeSystem } = require('../lib/api');
|
|
8
|
+
const { getInstanceId, deleteWorkflow } = require('../lib/db');
|
|
9
|
+
const { getJsonMetadata, detectComponentType } = require('../lib/workflow');
|
|
10
|
+
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
|
+
};
|
|
18
39
|
|
|
19
40
|
async function syncCommand() {
|
|
20
|
-
|
|
41
|
+
LOG.header('SYSTEM SYNC - Add Missing Components');
|
|
21
42
|
|
|
22
43
|
const projectRoot = config.get('PROJECT_ROOT');
|
|
23
44
|
const autoDiscover = config.get('AUTO_DISCOVER');
|
|
24
45
|
|
|
25
46
|
if (!autoDiscover) {
|
|
26
|
-
|
|
47
|
+
LOG.warning('AUTO_DISCOVER is disabled. To enable:');
|
|
27
48
|
console.log(chalk.dim(' workflow config set AUTO_DISCOVER true\n'));
|
|
28
49
|
return;
|
|
29
50
|
}
|
|
30
51
|
|
|
52
|
+
// Get domain from vnext.config.json
|
|
53
|
+
let domain;
|
|
54
|
+
try {
|
|
55
|
+
domain = getDomain(projectRoot);
|
|
56
|
+
} catch (error) {
|
|
57
|
+
LOG.error(`Failed to read vnext.config.json: ${error.message}`);
|
|
58
|
+
return;
|
|
59
|
+
}
|
|
60
|
+
|
|
31
61
|
// DB Config
|
|
62
|
+
const useDockerValue = config.get('USE_DOCKER');
|
|
32
63
|
const dbConfig = {
|
|
33
64
|
host: config.get('DB_HOST'),
|
|
34
65
|
port: config.get('DB_PORT'),
|
|
35
66
|
database: config.get('DB_NAME'),
|
|
36
67
|
user: config.get('DB_USER'),
|
|
37
68
|
password: config.get('DB_PASSWORD'),
|
|
38
|
-
useDocker:
|
|
69
|
+
useDocker: useDockerValue === true || useDockerValue === 'true',
|
|
39
70
|
dockerContainer: config.get('DOCKER_POSTGRES_CONTAINER')
|
|
40
71
|
};
|
|
41
72
|
|
|
42
73
|
// API Config
|
|
43
74
|
const apiConfig = {
|
|
44
75
|
baseUrl: config.get('API_BASE_URL'),
|
|
45
|
-
version: config.get('API_VERSION')
|
|
76
|
+
version: config.get('API_VERSION'),
|
|
77
|
+
domain: domain
|
|
46
78
|
};
|
|
47
79
|
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
80
|
+
console.log(chalk.dim(` Domain: ${domain}`));
|
|
81
|
+
console.log(chalk.dim(` API: ${apiConfig.baseUrl}`));
|
|
82
|
+
console.log();
|
|
83
|
+
|
|
84
|
+
// Discover folders
|
|
85
|
+
const discoverSpinner = ora('Scanning folders...').start();
|
|
86
|
+
let discovered;
|
|
87
|
+
try {
|
|
88
|
+
discovered = await discoverComponents(projectRoot);
|
|
89
|
+
discoverSpinner.succeed(chalk.green('Folders discovered'));
|
|
90
|
+
} catch (error) {
|
|
91
|
+
discoverSpinner.fail(chalk.red(`Folder scan error: ${error.message}`));
|
|
92
|
+
return;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
// FIRST: Update all CSX files
|
|
96
|
+
const csxSpinner = ora('Finding CSX files...').start();
|
|
97
|
+
let csxFiles;
|
|
98
|
+
try {
|
|
99
|
+
csxFiles = await findAllCsx(projectRoot);
|
|
100
|
+
csxSpinner.succeed(chalk.green(`${csxFiles.length} CSX files found`));
|
|
101
|
+
} catch (error) {
|
|
102
|
+
csxSpinner.warn(chalk.yellow(`CSX scan error: ${error.message}`));
|
|
103
|
+
csxFiles = [];
|
|
104
|
+
}
|
|
52
105
|
|
|
53
|
-
//
|
|
54
|
-
const
|
|
55
|
-
const csxFiles = await findAllCsx(projectRoot);
|
|
56
|
-
csxSpinner.succeed(chalk.green(`${csxFiles.length} CSX dosyası bulundu`));
|
|
106
|
+
// Update CSX files
|
|
107
|
+
const csxResults = { success: 0, failed: 0, errors: [] };
|
|
57
108
|
|
|
58
|
-
// CSX dosyalarını güncelle
|
|
59
109
|
if (csxFiles.length > 0) {
|
|
60
|
-
console.log(chalk.blue('\n
|
|
110
|
+
console.log(chalk.blue('\n Writing CSX files to JSONs...\n'));
|
|
61
111
|
|
|
62
|
-
let csxSuccessCount = 0;
|
|
63
112
|
for (const csxFile of csxFiles) {
|
|
64
113
|
const fileName = path.basename(csxFile);
|
|
65
|
-
const csxFileSpinner = ora(`Base64 encode: ${fileName}`).start();
|
|
66
114
|
|
|
67
115
|
try {
|
|
68
116
|
const result = await processCsxFile(csxFile, projectRoot);
|
|
69
117
|
|
|
70
118
|
if (result.success) {
|
|
71
|
-
|
|
72
|
-
|
|
119
|
+
LOG.component('CSX', fileName, 'success', `→ ${result.updatedJsonCount} JSON, ${result.totalUpdates} refs`);
|
|
120
|
+
csxResults.success++;
|
|
73
121
|
} else {
|
|
74
|
-
|
|
122
|
+
LOG.component('CSX', fileName, 'skip', result.message);
|
|
75
123
|
}
|
|
76
124
|
} catch (error) {
|
|
77
|
-
|
|
125
|
+
LOG.component('CSX', fileName, 'error', error.message);
|
|
126
|
+
csxResults.failed++;
|
|
127
|
+
csxResults.errors.push({ file: fileName, error: error.message });
|
|
78
128
|
}
|
|
79
129
|
}
|
|
80
|
-
|
|
81
|
-
if (csxSuccessCount > 0) {
|
|
82
|
-
console.log(chalk.green(`\n✓ ${csxSuccessCount} CSX dosyası güncellendi\n`));
|
|
83
|
-
}
|
|
84
130
|
}
|
|
85
131
|
|
|
86
|
-
//
|
|
87
|
-
const findSpinner = ora('JSON
|
|
88
|
-
|
|
89
|
-
|
|
132
|
+
// Find all JSON files
|
|
133
|
+
const findSpinner = ora('Finding JSON files...').start();
|
|
134
|
+
let allJsonFiles;
|
|
135
|
+
try {
|
|
136
|
+
allJsonFiles = await findAllJsonFiles(discovered);
|
|
137
|
+
findSpinner.succeed(chalk.green(`${allJsonFiles.length} JSON files found`));
|
|
138
|
+
} catch (error) {
|
|
139
|
+
findSpinner.fail(chalk.red(`JSON scan error: ${error.message}`));
|
|
140
|
+
return;
|
|
141
|
+
}
|
|
90
142
|
|
|
91
|
-
//
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
let failedCount = 0;
|
|
143
|
+
// Group by component type
|
|
144
|
+
const componentStats = {};
|
|
145
|
+
const errors = [];
|
|
95
146
|
|
|
96
|
-
console.log();
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
const
|
|
147
|
+
console.log(chalk.blue('\n Publishing components...\n'));
|
|
148
|
+
|
|
149
|
+
for (const jsonInfo of allJsonFiles) {
|
|
150
|
+
const { path: jsonPath, type, fileName } = jsonInfo;
|
|
151
|
+
|
|
152
|
+
// Initialize stats
|
|
153
|
+
if (!componentStats[type]) {
|
|
154
|
+
componentStats[type] = { success: 0, failed: 0, skipped: 0, existing: 0 };
|
|
155
|
+
}
|
|
100
156
|
|
|
101
157
|
try {
|
|
102
|
-
const metadata = await getJsonMetadata(
|
|
158
|
+
const metadata = await getJsonMetadata(jsonPath);
|
|
103
159
|
|
|
104
160
|
if (!metadata.key || !metadata.version) {
|
|
105
|
-
|
|
106
|
-
|
|
161
|
+
LOG.component(type, fileName, 'skip', 'no key/version');
|
|
162
|
+
componentStats[type].skipped++;
|
|
107
163
|
continue;
|
|
108
164
|
}
|
|
109
165
|
|
|
110
|
-
|
|
166
|
+
// Detect flow type
|
|
167
|
+
const flow = metadata.flow || detectComponentType(jsonPath, projectRoot);
|
|
111
168
|
|
|
112
|
-
//
|
|
169
|
+
// Check if exists in DB
|
|
113
170
|
const existingId = await getInstanceId(dbConfig, flow, metadata.key, metadata.version);
|
|
114
171
|
|
|
115
172
|
if (existingId) {
|
|
116
|
-
|
|
117
|
-
|
|
173
|
+
// Already exists, skip
|
|
174
|
+
LOG.component(type, fileName, 'skip', 'already exists');
|
|
175
|
+
componentStats[type].existing++;
|
|
176
|
+
continue;
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
// Not in DB, publish to API
|
|
180
|
+
const result = await publishComponent(apiConfig.baseUrl, metadata.data);
|
|
181
|
+
|
|
182
|
+
if (result.success) {
|
|
183
|
+
LOG.component(type, fileName, 'success', '→ published');
|
|
184
|
+
componentStats[type].success++;
|
|
118
185
|
} else {
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
apiConfig.version,
|
|
123
|
-
flow,
|
|
124
|
-
metadata.data
|
|
125
|
-
);
|
|
126
|
-
|
|
127
|
-
const newId = postResult.id || postResult.Id;
|
|
128
|
-
|
|
129
|
-
// Aktif et
|
|
130
|
-
await activateWorkflow(
|
|
131
|
-
apiConfig.baseUrl,
|
|
132
|
-
apiConfig.version,
|
|
133
|
-
flow,
|
|
134
|
-
newId,
|
|
135
|
-
metadata.version
|
|
136
|
-
);
|
|
137
|
-
|
|
138
|
-
spinner.succeed(chalk.green(`✓ ${fileName} → eklendi`));
|
|
139
|
-
addedCount++;
|
|
186
|
+
LOG.component(type, fileName, 'error', result.error);
|
|
187
|
+
componentStats[type].failed++;
|
|
188
|
+
errors.push({ type, file: fileName, error: result.error });
|
|
140
189
|
}
|
|
141
190
|
} catch (error) {
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
} else if (error.response.data.message) {
|
|
147
|
-
errorMsg = error.response.data.message;
|
|
148
|
-
} else {
|
|
149
|
-
errorMsg = JSON.stringify(error.response.data);
|
|
150
|
-
}
|
|
151
|
-
}
|
|
152
|
-
spinner.fail(chalk.red(`✗ ${fileName} → ${errorMsg}`));
|
|
153
|
-
failedCount++;
|
|
191
|
+
const errorMsg = error.message || 'Unknown error';
|
|
192
|
+
LOG.component(type, fileName, 'error', errorMsg);
|
|
193
|
+
componentStats[type].failed++;
|
|
194
|
+
errors.push({ type, file: fileName, error: errorMsg });
|
|
154
195
|
}
|
|
155
196
|
}
|
|
156
197
|
|
|
157
198
|
// Re-initialize
|
|
158
|
-
|
|
199
|
+
const totalSuccess = Object.values(componentStats).reduce((sum, s) => sum + s.success, 0);
|
|
200
|
+
|
|
201
|
+
if (totalSuccess > 0) {
|
|
159
202
|
console.log();
|
|
160
|
-
const reinitSpinner = ora('
|
|
203
|
+
const reinitSpinner = ora('Re-initializing system...').start();
|
|
161
204
|
const reinitSuccess = await reinitializeSystem(apiConfig.baseUrl, apiConfig.version);
|
|
162
205
|
|
|
163
206
|
if (reinitSuccess) {
|
|
164
|
-
reinitSpinner.succeed(chalk.green('
|
|
207
|
+
reinitSpinner.succeed(chalk.green('System re-initialized'));
|
|
165
208
|
} else {
|
|
166
|
-
reinitSpinner.warn(chalk.yellow('
|
|
209
|
+
reinitSpinner.warn(chalk.yellow('System re-initialization failed'));
|
|
167
210
|
}
|
|
168
211
|
}
|
|
169
212
|
|
|
170
|
-
//
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
console.log(chalk.
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
213
|
+
// SUMMARY REPORT
|
|
214
|
+
LOG.header('SYNC SUMMARY');
|
|
215
|
+
|
|
216
|
+
// Component statistics
|
|
217
|
+
console.log(chalk.white.bold('\n Component Publish Results:\n'));
|
|
218
|
+
|
|
219
|
+
const componentTypes = getComponentTypes(projectRoot);
|
|
220
|
+
for (const [type, folderName] of Object.entries(componentTypes)) {
|
|
221
|
+
const stats = componentStats[type];
|
|
222
|
+
if (stats) {
|
|
223
|
+
const successLabel = stats.success > 0 ? chalk.green(`${stats.success} added`) : '';
|
|
224
|
+
const existingLabel = stats.existing > 0 ? chalk.dim(`${stats.existing} existing`) : '';
|
|
225
|
+
const failedLabel = stats.failed > 0 ? chalk.red(`${stats.failed} failed`) : '';
|
|
226
|
+
const skippedLabel = stats.skipped > 0 ? chalk.dim(`${stats.skipped} skipped`) : '';
|
|
227
|
+
|
|
228
|
+
const parts = [successLabel, existingLabel, failedLabel, skippedLabel].filter(Boolean);
|
|
229
|
+
console.log(` ${chalk.cyan(type.padEnd(12))} : ${parts.join(', ') || chalk.dim('0')}`);
|
|
230
|
+
}
|
|
178
231
|
}
|
|
179
|
-
console.log(chalk.cyan('═'.repeat(50)));
|
|
180
|
-
console.log();
|
|
181
232
|
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
233
|
+
// CSX summary
|
|
234
|
+
if (csxFiles.length > 0) {
|
|
235
|
+
console.log();
|
|
236
|
+
const csxSuccessLabel = csxResults.success > 0 ? chalk.green(`${csxResults.success} success`) : chalk.dim('0 success');
|
|
237
|
+
const csxFailedLabel = csxResults.failed > 0 ? chalk.red(`, ${csxResults.failed} failed`) : '';
|
|
238
|
+
console.log(` ${chalk.cyan('CSX'.padEnd(12))} : ${csxSuccessLabel}${csxFailedLabel}`);
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
// Errors
|
|
242
|
+
if (errors.length > 0 || csxResults.errors.length > 0) {
|
|
243
|
+
console.log();
|
|
244
|
+
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
|
+
}
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
LOG.separator();
|
|
259
|
+
|
|
260
|
+
const totalFailed = Object.values(componentStats).reduce((sum, s) => sum + s.failed, 0) + csxResults.failed;
|
|
261
|
+
|
|
262
|
+
if (totalSuccess === 0 && totalFailed === 0) {
|
|
263
|
+
console.log(chalk.green.bold('\n ✓ System up to date - All records exist\n'));
|
|
264
|
+
} else if (totalFailed === 0) {
|
|
265
|
+
console.log(chalk.green.bold('\n ✓ Sync completed\n'));
|
|
266
|
+
} else {
|
|
267
|
+
console.log(chalk.yellow.bold(`\n ⚠ Sync completed (${totalFailed} errors)\n`));
|
|
186
268
|
}
|
|
187
269
|
}
|
|
188
270
|
|