@burgan-tech/vnext-workflow-cli 1.0.1 → 1.0.3

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.
@@ -1,65 +1,167 @@
1
1
  const { glob } = require('glob');
2
2
  const path = require('path');
3
3
  const fs = require('fs');
4
-
5
- const COMPONENTS = ['Workflows', 'Tasks', 'Schemas', 'Views', 'Functions', 'Extensions'];
4
+ const { getComponentsRoot, getComponentTypes } = require('./vnextConfig');
6
5
 
7
6
  /**
8
- * Proje içinde component klasörlerini otomatik bulur
7
+ * Discovers component folders based on vnext.config.json paths
8
+ * Only scans folders defined in paths, ignores everything else
9
+ * @param {string} projectRoot - Project root folder (PROJECT_ROOT)
10
+ * @returns {Object} Discovered component folders
9
11
  */
10
12
  async function discoverComponents(projectRoot) {
13
+ const componentsRoot = getComponentsRoot(projectRoot);
14
+ const componentTypes = getComponentTypes(projectRoot);
15
+
11
16
  const discovered = {};
12
17
 
13
- for (const component of COMPONENTS) {
14
- try {
15
- // 3 seviye derinlikte ara
16
- const pattern = path.join(projectRoot, '**', component);
17
- const matches = await glob(pattern, {
18
- maxDepth: 3,
19
- ignore: ['**/node_modules/**', '**/dist/**', '**/.git/**']
20
- });
18
+ // Only look for folders defined in paths
19
+ for (const [type, folderName] of Object.entries(componentTypes)) {
20
+ const componentDir = path.join(componentsRoot, folderName);
21
+
22
+ if (fs.existsSync(componentDir) && fs.statSync(componentDir).isDirectory()) {
23
+ discovered[type] = componentDir;
24
+ }
25
+ }
26
+
27
+ return discovered;
28
+ }
29
+
30
+ /**
31
+ * Finds all JSON files in a specific component folder
32
+ * Scans subfolders recursively
33
+ * Ignores .meta folders and *.diagram.json files
34
+ * @param {string} componentDir - Component folder
35
+ * @returns {Promise<string[]>} JSON file paths
36
+ */
37
+ async function findJsonInComponent(componentDir) {
38
+ const pattern = path.join(componentDir, '**/*.json');
39
+
40
+ const files = await glob(pattern, {
41
+ ignore: [
42
+ '**/.meta/**',
43
+ '**/.meta',
44
+ '**/*.diagram.json',
45
+ '**/package*.json',
46
+ '**/*config*.json'
47
+ ]
48
+ });
49
+
50
+ return files;
51
+ }
52
+
53
+ /**
54
+ * Finds all JSON files in discovered components ONLY
55
+ * Does NOT scan folders outside of paths definition
56
+ * @param {Object} discovered - Discovered component folders
57
+ * @returns {Promise<Object[]>} JSON file info (path, type, fileName)
58
+ */
59
+ async function findAllJsonFiles(discovered) {
60
+ const allFiles = [];
61
+
62
+ // Only scan folders that were discovered from paths
63
+ for (const [type, componentDir] of Object.entries(discovered)) {
64
+ if (componentDir) {
65
+ const files = await findJsonInComponent(componentDir);
21
66
 
22
- if (matches.length > 0) {
23
- // İlk bulunanı al
24
- const dir = matches[0];
25
- if (fs.existsSync(dir) && fs.statSync(dir).isDirectory()) {
26
- discovered[component] = dir;
27
- }
67
+ for (const file of files) {
68
+ allFiles.push({
69
+ path: file,
70
+ type: type,
71
+ fileName: path.basename(file)
72
+ });
28
73
  }
29
- } catch (error) {
30
- // Ignore errors
31
74
  }
32
75
  }
33
76
 
34
- return discovered;
77
+ return allFiles;
35
78
  }
36
79
 
37
80
  /**
38
- * Belirli bir component klasörünü getirir
81
+ * Finds all CSX files in discovered components ONLY
82
+ * Does NOT scan folders outside of paths definition
83
+ * @param {string} projectRoot - Project root folder
84
+ * @returns {Promise<string[]>} CSX file paths
85
+ */
86
+ async function findAllCsxInComponents(projectRoot) {
87
+ const discovered = await discoverComponents(projectRoot);
88
+ const allCsxFiles = [];
89
+
90
+ // Only scan folders that were discovered from paths
91
+ for (const [type, componentDir] of Object.entries(discovered)) {
92
+ if (componentDir) {
93
+ const pattern = path.join(componentDir, '**/*.csx');
94
+ const files = await glob(pattern, {
95
+ ignore: [
96
+ '**/.meta/**',
97
+ '**/.meta',
98
+ '**/node_modules/**',
99
+ '**/dist/**'
100
+ ]
101
+ });
102
+ allCsxFiles.push(...files);
103
+ }
104
+ }
105
+
106
+ return allCsxFiles;
107
+ }
108
+
109
+ /**
110
+ * Gets a specific component folder
111
+ * @param {Object} discovered - Discovered component folders
112
+ * @param {string} component - Component type
113
+ * @returns {string|null} Folder path
39
114
  */
40
115
  function getComponentDir(discovered, component) {
41
116
  return discovered[component] || null;
42
117
  }
43
118
 
44
119
  /**
45
- * Bulunan klasörleri listeler
120
+ * Lists discovered folders
121
+ * @param {Object} discovered - Discovered component folders
122
+ * @param {Object} componentTypes - All component types from paths
123
+ * @returns {Object[]} Folder list
46
124
  */
47
- function listDiscovered(discovered) {
125
+ function listDiscovered(discovered, componentTypes) {
48
126
  const results = [];
49
- for (const component of COMPONENTS) {
127
+
128
+ for (const [type, folderName] of Object.entries(componentTypes)) {
50
129
  results.push({
51
- name: component,
52
- path: discovered[component] || null,
53
- found: !!discovered[component]
130
+ name: type,
131
+ folderName: folderName,
132
+ path: discovered[type] || null,
133
+ found: !!discovered[type]
54
134
  });
55
135
  }
136
+
56
137
  return results;
57
138
  }
58
139
 
140
+ /**
141
+ * Detects component type from file path
142
+ * @param {string} filePath - File path
143
+ * @param {Object} componentTypes - Component type -> folder name mapping
144
+ * @returns {string} Component type
145
+ */
146
+ function detectComponentTypeFromPath(filePath, componentTypes) {
147
+ const normalizedPath = filePath.toLowerCase();
148
+
149
+ for (const [type, folderName] of Object.entries(componentTypes)) {
150
+ const folderPattern = `/${folderName.toLowerCase()}/`;
151
+ if (normalizedPath.includes(folderPattern)) {
152
+ return type;
153
+ }
154
+ }
155
+
156
+ return 'unknown';
157
+ }
158
+
59
159
  module.exports = {
60
160
  discoverComponents,
161
+ findJsonInComponent,
162
+ findAllJsonFiles,
163
+ findAllCsxInComponents,
61
164
  getComponentDir,
62
165
  listDiscovered,
63
- COMPONENTS
166
+ detectComponentTypeFromPath
64
167
  };
65
-
@@ -0,0 +1,124 @@
1
+ const fs = require('fs');
2
+ const path = require('path');
3
+
4
+ let cachedConfig = null;
5
+ let cachedProjectRoot = null;
6
+
7
+ /**
8
+ * Reads and parses vnext.config.json file
9
+ * @param {string} projectRoot - Project root folder
10
+ * @returns {Object} vnext.config.json content
11
+ */
12
+ function loadVnextConfig(projectRoot) {
13
+ // Cache check
14
+ if (cachedConfig && cachedProjectRoot === projectRoot) {
15
+ return cachedConfig;
16
+ }
17
+
18
+ const configPath = path.join(projectRoot, 'vnext.config.json');
19
+
20
+ if (!fs.existsSync(configPath)) {
21
+ throw new Error(`vnext.config.json not found: ${configPath}`);
22
+ }
23
+
24
+ try {
25
+ const content = fs.readFileSync(configPath, 'utf8');
26
+ cachedConfig = JSON.parse(content);
27
+ cachedProjectRoot = projectRoot;
28
+ return cachedConfig;
29
+ } catch (error) {
30
+ throw new Error(`Failed to read vnext.config.json: ${error.message}`);
31
+ }
32
+ }
33
+
34
+ /**
35
+ * Returns domain information
36
+ * @param {string} projectRoot - Project root folder
37
+ * @returns {string} Domain name
38
+ */
39
+ function getDomain(projectRoot) {
40
+ const config = loadVnextConfig(projectRoot);
41
+
42
+ if (!config.domain) {
43
+ throw new Error('domain not found in vnext.config.json');
44
+ }
45
+
46
+ return config.domain;
47
+ }
48
+
49
+ /**
50
+ * Returns paths information
51
+ * @param {string} projectRoot - Project root folder
52
+ * @returns {Object} Paths object
53
+ */
54
+ function getPaths(projectRoot) {
55
+ const config = loadVnextConfig(projectRoot);
56
+
57
+ if (!config.paths) {
58
+ throw new Error('paths not found in vnext.config.json');
59
+ }
60
+
61
+ return config.paths;
62
+ }
63
+
64
+ /**
65
+ * Returns components root folder
66
+ * @param {string} projectRoot - Project root folder
67
+ * @returns {string} Components root folder path
68
+ */
69
+ function getComponentsRoot(projectRoot) {
70
+ const paths = getPaths(projectRoot);
71
+
72
+ if (!paths.componentsRoot) {
73
+ throw new Error('paths.componentsRoot not found in vnext.config.json');
74
+ }
75
+
76
+ return path.join(projectRoot, paths.componentsRoot);
77
+ }
78
+
79
+ /**
80
+ * Returns component types and folder names
81
+ * @param {string} projectRoot - Project root folder
82
+ * @returns {Object} Component type -> folder name mapping
83
+ */
84
+ function getComponentTypes(projectRoot) {
85
+ const paths = getPaths(projectRoot);
86
+
87
+ // All paths except componentsRoot are component types
88
+ const componentTypes = {};
89
+
90
+ for (const [key, value] of Object.entries(paths)) {
91
+ if (key !== 'componentsRoot') {
92
+ componentTypes[key] = value;
93
+ }
94
+ }
95
+
96
+ return componentTypes;
97
+ }
98
+
99
+ /**
100
+ * Returns full configuration
101
+ * @param {string} projectRoot - Project root folder
102
+ * @returns {Object} Full vnext.config.json content
103
+ */
104
+ function getFullConfig(projectRoot) {
105
+ return loadVnextConfig(projectRoot);
106
+ }
107
+
108
+ /**
109
+ * Clears the cache
110
+ */
111
+ function clearCache() {
112
+ cachedConfig = null;
113
+ cachedProjectRoot = null;
114
+ }
115
+
116
+ module.exports = {
117
+ loadVnextConfig,
118
+ getDomain,
119
+ getPaths,
120
+ getComponentsRoot,
121
+ getComponentTypes,
122
+ getFullConfig,
123
+ clearCache
124
+ };
@@ -1,11 +1,15 @@
1
1
  const fs = require('fs').promises;
2
2
  const path = require('path');
3
3
  const { glob } = require('glob');
4
+ const { publishComponent } = require('./api');
4
5
  const { getInstanceId, deleteWorkflow } = require('./db');
5
- const { postWorkflow, activateWorkflow, reinitializeSystem } = require('./api');
6
+ const { getComponentTypes } = require('./vnextConfig');
7
+ const { discoverComponents, findJsonInComponent } = require('./discover');
6
8
 
7
9
  /**
8
- * JSON dosyasından key ve version değerlerini alır
10
+ * Gets key and version values from JSON file
11
+ * @param {string} jsonPath - JSON file path
12
+ * @returns {Promise<Object>} Metadata object
9
13
  */
10
14
  async function getJsonMetadata(jsonPath) {
11
15
  const content = await fs.readFile(jsonPath, 'utf8');
@@ -20,11 +24,38 @@ async function getJsonMetadata(jsonPath) {
20
24
  }
21
25
 
22
26
  /**
23
- * Dosya yolundan flow tipini belirler
27
+ * Detects component type from file path based on vnext.config.json paths
28
+ * @param {string} jsonPath - JSON file path
29
+ * @param {string} projectRoot - Project root folder
30
+ * @returns {string} Component type (sys-flows, sys-tasks, etc.)
24
31
  */
25
- function detectFlowFromPath(jsonPath) {
32
+ function detectComponentType(jsonPath, projectRoot) {
26
33
  const pathLower = jsonPath.toLowerCase();
27
34
 
35
+ try {
36
+ const componentTypes = getComponentTypes(projectRoot);
37
+
38
+ // Check each component type folder
39
+ for (const [type, folderName] of Object.entries(componentTypes)) {
40
+ const folderPattern = `/${folderName.toLowerCase()}/`;
41
+ if (pathLower.includes(folderPattern)) {
42
+ // Map to flow type
43
+ switch (type.toLowerCase()) {
44
+ case 'workflows': return 'sys-flows';
45
+ case 'tasks': return 'sys-tasks';
46
+ case 'schemas': return 'sys-schemas';
47
+ case 'views': return 'sys-views';
48
+ case 'functions': return 'sys-functions';
49
+ case 'extensions': return 'sys-extensions';
50
+ default: return `sys-${type.toLowerCase()}`;
51
+ }
52
+ }
53
+ }
54
+ } catch (error) {
55
+ // Fallback to path-based detection
56
+ }
57
+
58
+ // Fallback: detect from path directly
28
59
  if (pathLower.includes('/workflows/')) return 'sys-flows';
29
60
  if (pathLower.includes('/tasks/')) return 'sys-tasks';
30
61
  if (pathLower.includes('/schemas/')) return 'sys-schemas';
@@ -36,63 +67,67 @@ function detectFlowFromPath(jsonPath) {
36
67
  }
37
68
 
38
69
  /**
39
- * Tek bir workflow'u işler (DB'den sil, POST, Activate)
70
+ * Processes a single component (DB check delete if exists → publish)
71
+ * @param {string} jsonPath - JSON file path
72
+ * @param {Object} dbConfig - Database configuration
73
+ * @param {string} baseUrl - API base URL
74
+ * @param {string} projectRoot - Project root folder
75
+ * @returns {Promise<Object>} Process result
40
76
  */
41
- async function processWorkflow(jsonPath, dbConfig, apiConfig) {
77
+ async function processComponent(jsonPath, dbConfig, baseUrl, projectRoot) {
42
78
  const metadata = await getJsonMetadata(jsonPath);
43
79
 
44
80
  if (!metadata.key || !metadata.version) {
45
- throw new Error('JSON\'da key veya version bulunamadı');
81
+ throw new Error('No key or version found in JSON');
46
82
  }
47
83
 
48
- const flow = metadata.flow || detectFlowFromPath(jsonPath);
84
+ const componentType = detectComponentType(jsonPath, projectRoot);
85
+ const flow = metadata.flow || componentType;
49
86
 
50
- // 1. DB'de var kontrol et
51
- const instanceId = await getInstanceId(dbConfig, flow, metadata.key, metadata.version);
87
+ // 1. Check if exists in DB
88
+ const existingId = await getInstanceId(dbConfig, flow, metadata.key, metadata.version);
52
89
 
53
- // 2. Varsa sil
54
- if (instanceId) {
55
- await deleteWorkflow(dbConfig, flow, instanceId);
90
+ // 2. If exists, delete first
91
+ let wasDeleted = false;
92
+ if (existingId) {
93
+ await deleteWorkflow(dbConfig, flow, existingId);
94
+ wasDeleted = true;
56
95
  }
57
96
 
58
- // 3. API'ye POST et
59
- const apiUrl = apiConfig.baseUrl;
60
- const apiVersion = apiConfig.version;
61
-
62
- const postResult = await postWorkflow(apiUrl, apiVersion, flow, metadata.data);
63
- const newInstanceId = postResult.id || postResult.Id;
97
+ // 3. Publish to API
98
+ const result = await publishComponent(baseUrl, metadata.data);
64
99
 
65
- if (!newInstanceId) {
66
- throw new Error('API POST başarılı ama instance ID alınamadı');
100
+ if (!result.success) {
101
+ throw new Error(result.error);
67
102
  }
68
103
 
69
- // 4. Aktif et
70
- await activateWorkflow(apiUrl, apiVersion, flow, newInstanceId, metadata.version);
71
-
72
104
  return {
73
105
  key: metadata.key,
74
106
  version: metadata.version,
75
- flow: flow,
76
- instanceId: newInstanceId,
77
- wasDeleted: !!instanceId
107
+ componentType: componentType,
108
+ wasDeleted: wasDeleted,
109
+ success: true
78
110
  };
79
111
  }
80
112
 
81
113
  /**
82
- * Git'te değişen JSON dosyalarını bulur
114
+ * Finds changed JSON files in Git
115
+ * Only returns files within PROJECT_ROOT
116
+ * @param {string} projectRoot - Project root folder
117
+ * @returns {Promise<string[]>} Changed JSON file paths
83
118
  */
84
119
  async function getGitChangedJson(projectRoot) {
85
120
  const { exec } = require('child_process');
86
121
  const util = require('util');
87
122
  const execPromise = util.promisify(exec);
88
- const fs = require('fs');
123
+ const fsSync = require('fs');
89
124
 
90
125
  try {
91
- // Git root'u bul
126
+ // Find git root
92
127
  const { stdout: gitRoot } = await execPromise('git rev-parse --show-toplevel', { cwd: projectRoot });
93
128
  const gitRootDir = gitRoot.trim();
94
129
 
95
- // Git status'u git root'tan çalıştır
130
+ // Run git status from git root
96
131
  const { stdout } = await execPromise('git status --porcelain', { cwd: gitRootDir });
97
132
  const lines = stdout.split('\n').filter(Boolean);
98
133
 
@@ -108,12 +143,13 @@ async function getGitChangedJson(projectRoot) {
108
143
  return path.normalize(fullPath);
109
144
  })
110
145
  .filter(file => {
111
- // Workflow JSON'larını filtrele ve sadece project içindekileri al
146
+ // Filter workflow JSONs and only those in project
112
147
  const fileName = path.basename(file);
113
148
  return file.endsWith('.json') &&
114
149
  !fileName.includes('package') &&
115
150
  !fileName.includes('config') &&
116
- fs.existsSync(file) &&
151
+ !fileName.includes('.diagram.') &&
152
+ fsSync.existsSync(file) &&
117
153
  file.startsWith(path.normalize(projectRoot));
118
154
  });
119
155
 
@@ -124,22 +160,34 @@ async function getGitChangedJson(projectRoot) {
124
160
  }
125
161
 
126
162
  /**
127
- * Belirli bir component klasöründeki tüm JSON dosyalarını bulur
163
+ * Finds all JSON files in a component folder
164
+ * @param {string} componentDir - Component folder
165
+ * @returns {Promise<string[]>} JSON file paths
128
166
  */
129
167
  async function findAllJsonInComponent(componentDir) {
130
168
  const pattern = path.join(componentDir, '**/*.json');
131
169
  const files = await glob(pattern, {
132
- ignore: ['**/package*.json', '**/*config*.json', '**/*.diagram.json']
170
+ ignore: [
171
+ '**/.meta/**',
172
+ '**/.meta',
173
+ '**/*.diagram.json',
174
+ '**/package*.json',
175
+ '**/*config*.json'
176
+ ]
133
177
  });
134
178
  return files;
135
179
  }
136
180
 
137
181
  /**
138
- * Tüm component'lerdeki JSON dosyalarını bulur
182
+ * Finds all JSON files in discovered components ONLY
183
+ * Does NOT scan folders outside of paths definition
184
+ * @param {Object} discovered - Discovered component folders
185
+ * @returns {Promise<string[]>} JSON file paths
139
186
  */
140
187
  async function findAllJson(discovered) {
141
188
  const allJsons = [];
142
189
 
190
+ // Only scan folders that were discovered from paths
143
191
  for (const component in discovered) {
144
192
  const componentDir = discovered[component];
145
193
  if (componentDir) {
@@ -153,10 +201,9 @@ async function findAllJson(discovered) {
153
201
 
154
202
  module.exports = {
155
203
  getJsonMetadata,
156
- detectFlowFromPath,
157
- processWorkflow,
204
+ detectComponentType,
205
+ processComponent,
158
206
  getGitChangedJson,
159
207
  findAllJsonInComponent,
160
208
  findAllJson
161
209
  };
162
-