@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/src/lib/csx.js CHANGED
@@ -1,9 +1,12 @@
1
1
  const fs = require('fs').promises;
2
2
  const path = require('path');
3
3
  const { glob } = require('glob');
4
+ const { discoverComponents, findAllJsonFiles } = require('./discover');
4
5
 
5
6
  /**
6
- * CSX dosyasını Base64'e çevirir
7
+ * Encodes CSX file to Base64
8
+ * @param {string} csxPath - CSX file path
9
+ * @returns {Promise<string>} Base64 encoded content
7
10
  */
8
11
  async function encodeToBase64(csxPath) {
9
12
  const content = await fs.readFile(csxPath, 'utf8');
@@ -11,28 +14,32 @@ async function encodeToBase64(csxPath) {
11
14
  }
12
15
 
13
16
  /**
14
- * CSX dosyası için ilgili JSON dosyalarını bulur
17
+ * Finds JSON files that reference the CSX file
18
+ * Only searches in paths defined in vnext.config.json
19
+ * @param {string} csxPath - CSX file path
20
+ * @param {string} projectRoot - Project root folder
21
+ * @returns {Promise<string[]>} Matching JSON file paths
15
22
  */
16
23
  async function findJsonFilesForCsx(csxPath, projectRoot) {
17
24
  const csxBaseName = path.basename(csxPath);
18
25
 
19
- // Tüm JSON dosyalarını bul
20
- const pattern = path.join(projectRoot, '**', '*.json');
21
- const jsonFiles = await glob(pattern, {
22
- ignore: ['**/node_modules/**', '**/dist/**', '**/package*.json', '**/*config*.json']
23
- });
26
+ // Get discovered components (only paths defined in vnext.config.json)
27
+ const discovered = await discoverComponents(projectRoot);
24
28
 
25
- // CSX referansı olan JSON'ları filtrele
29
+ // Get all JSON files from discovered components only
30
+ const jsonFileInfos = await findAllJsonFiles(discovered);
31
+
32
+ // Filter JSONs that reference the CSX
26
33
  const matchingJsons = [];
27
34
 
28
- for (const jsonFile of jsonFiles) {
35
+ for (const jsonInfo of jsonFileInfos) {
29
36
  try {
30
- const content = await fs.readFile(jsonFile, 'utf8');
37
+ const content = await fs.readFile(jsonInfo.path, 'utf8');
31
38
  if (content.includes(csxBaseName)) {
32
- matchingJsons.push(jsonFile);
39
+ matchingJsons.push(jsonInfo.path);
33
40
  }
34
41
  } catch (error) {
35
- // Ignore
42
+ // Skip unreadable files
36
43
  }
37
44
  }
38
45
 
@@ -40,11 +47,14 @@ async function findJsonFilesForCsx(csxPath, projectRoot) {
40
47
  }
41
48
 
42
49
  /**
43
- * CSX location path'ini hesaplar
50
+ * Calculates CSX location path
51
+ * @param {string} csxPath - CSX file path
52
+ * @param {string} projectRoot - Project root folder
53
+ * @returns {string} Location path
44
54
  */
45
- function getCsxLocation(csxPath) {
46
- // ./src/Rules/MyRule.csx formatına çevir
47
- const parts = csxPath.split('/');
55
+ function getCsxLocation(csxPath, projectRoot) {
56
+ // Convert to ./src/Rules/MyRule.csx format
57
+ const parts = csxPath.split(path.sep);
48
58
  const srcIndex = parts.lastIndexOf('src');
49
59
 
50
60
  if (srcIndex !== -1) {
@@ -56,91 +66,154 @@ function getCsxLocation(csxPath) {
56
66
  }
57
67
 
58
68
  /**
59
- * JSON dosyasında CSX code'unu günceller
69
+ * Updates ALL CSX codes in a JSON file
70
+ * Updates all references with the same location
71
+ * Supports encoding types: NAT (native/plain text), B64 (Base64, default)
72
+ * @param {string} jsonPath - JSON file path
73
+ * @param {string} csxLocation - CSX location path
74
+ * @param {string} base64Code - Base64 encoded CSX content
75
+ * @param {string} nativeCode - Native (plain text) CSX content
76
+ * @returns {Promise<number>} Number of updated references
60
77
  */
61
- async function updateCodeInJson(jsonPath, csxLocation, base64Code) {
78
+ async function updateCodeInJson(jsonPath, csxLocation, base64Code, nativeCode) {
62
79
  const content = await fs.readFile(jsonPath, 'utf8');
63
80
  const data = JSON.parse(content);
64
81
 
65
- // Recursive olarak location'ı bul ve güncelle
82
+ let updateCount = 0;
83
+
84
+ // Recursively find and update ALL location matches
66
85
  function updateRecursive(obj) {
67
- if (typeof obj !== 'object' || obj === null) return false;
86
+ if (typeof obj !== 'object' || obj === null) return;
87
+
88
+ // Skip if location is missing
89
+ if (!obj.location) {
90
+ // Continue scanning children
91
+ for (const key in obj) {
92
+ if (typeof obj[key] === 'object' && obj[key] !== null) {
93
+ updateRecursive(obj[key]);
94
+ }
95
+ }
96
+ return;
97
+ }
68
98
 
69
99
  if (obj.location === csxLocation && 'code' in obj) {
70
- obj.code = base64Code;
71
- return true;
100
+ // Check encoding type: NAT = Native (plain text), B64 or empty = Base64 (default)
101
+ const encoding = obj.encoding ? obj.encoding.toUpperCase() : 'B64';
102
+
103
+ if (encoding === 'NAT') {
104
+ // Native encoding - write plain text content
105
+ obj.code = nativeCode;
106
+ } else {
107
+ // B64 or default - write Base64 encoded content
108
+ obj.code = base64Code;
109
+ }
110
+ updateCount++;
72
111
  }
73
112
 
113
+ // Scan all elements in array or object
74
114
  for (const key in obj) {
75
- if (updateRecursive(obj[key])) {
76
- return true;
115
+ if (typeof obj[key] === 'object' && obj[key] !== null) {
116
+ updateRecursive(obj[key]);
77
117
  }
78
118
  }
79
-
80
- return false;
81
119
  }
82
120
 
83
- const updated = updateRecursive(data);
121
+ updateRecursive(data);
84
122
 
85
- if (updated) {
123
+ if (updateCount > 0) {
86
124
  await fs.writeFile(jsonPath, JSON.stringify(data, null, 2), 'utf8');
87
- return true;
88
125
  }
89
126
 
90
- return false;
127
+ return updateCount;
91
128
  }
92
129
 
93
130
  /**
94
- * Tek bir CSX dosyasını işler
131
+ * Reads CSX file content as plain text (native)
132
+ * @param {string} csxPath - CSX file path
133
+ * @returns {Promise<string>} Plain text content
134
+ */
135
+ async function readNativeContent(csxPath) {
136
+ return await fs.readFile(csxPath, 'utf8');
137
+ }
138
+
139
+ /**
140
+ * Processes a single CSX file
141
+ * Updates ALL referencing JSON files
142
+ * Supports both NAT (native) and B64 (Base64) encoding
143
+ * @param {string} csxPath - CSX file path
144
+ * @param {string} projectRoot - Project root folder
145
+ * @returns {Promise<Object>} Process result
95
146
  */
96
147
  async function processCsxFile(csxPath, projectRoot) {
97
- // Base64'e çevir
98
- const base64Code = await encodeToBase64(csxPath);
148
+ // Read native content
149
+ const nativeCode = await readNativeContent(csxPath);
99
150
 
100
- // İlgili JSON'ları bul
151
+ // Convert to Base64
152
+ const base64Code = Buffer.from(nativeCode).toString('base64');
153
+
154
+ // Find ALL related JSONs (only in paths defined in vnext.config.json)
101
155
  const jsonFiles = await findJsonFilesForCsx(csxPath, projectRoot);
102
156
 
103
157
  if (jsonFiles.length === 0) {
104
- return { success: false, message: 'İlgili JSON bulunamadı' };
158
+ return {
159
+ success: false,
160
+ message: 'No related JSON found',
161
+ updatedJsonCount: 0,
162
+ totalUpdates: 0,
163
+ jsonFiles: []
164
+ };
105
165
  }
106
166
 
107
- // CSX location'ı hesapla
108
- const csxLocation = getCsxLocation(csxPath);
167
+ // Calculate CSX location
168
+ const csxLocation = getCsxLocation(csxPath, projectRoot);
169
+
170
+ // Update each JSON
171
+ let updatedJsonCount = 0;
172
+ let totalUpdates = 0;
173
+ const updatedFiles = [];
109
174
 
110
- // Her JSON'u güncelle
111
- let updatedCount = 0;
112
175
  for (const jsonFile of jsonFiles) {
113
176
  try {
114
- const updated = await updateCodeInJson(jsonFile, csxLocation, base64Code);
115
- if (updated) {
116
- updatedCount++;
177
+ const updates = await updateCodeInJson(jsonFile, csxLocation, base64Code, nativeCode);
178
+ if (updates > 0) {
179
+ updatedJsonCount++;
180
+ totalUpdates += updates;
181
+ updatedFiles.push({
182
+ file: path.basename(jsonFile),
183
+ updates: updates
184
+ });
117
185
  }
118
186
  } catch (error) {
119
- // Continue with next file
187
+ // Continue with next file on error
120
188
  }
121
189
  }
122
190
 
123
191
  return {
124
- success: updatedCount > 0,
125
- updatedCount,
126
- jsonFiles: jsonFiles.map(f => path.basename(f))
192
+ success: updatedJsonCount > 0,
193
+ message: updatedJsonCount > 0 ? 'Updated' : 'No references to update',
194
+ updatedJsonCount,
195
+ totalUpdates,
196
+ jsonFiles: updatedFiles
127
197
  };
128
198
  }
129
199
 
130
200
  /**
131
- * Git'te değişen CSX dosyalarını bulur
201
+ * Finds changed CSX files in Git
202
+ * @param {string} projectRoot - Project root folder
203
+ * @returns {Promise<string[]>} Changed CSX file paths
132
204
  */
133
205
  async function getGitChangedCsx(projectRoot) {
134
206
  const { exec } = require('child_process');
135
207
  const util = require('util');
136
208
  const execPromise = util.promisify(exec);
209
+ const fsSync = require('fs');
137
210
 
138
211
  try {
139
- // Git root'u bul
212
+ // Find git root
140
213
  const { stdout: gitRoot } = await execPromise('git rev-parse --show-toplevel', { cwd: projectRoot });
141
214
  const gitRootDir = gitRoot.trim();
142
215
 
143
- // Git status'u git root'tan çalıştır
216
+ // Run git status from git root
144
217
  const { stdout } = await execPromise('git status --porcelain', { cwd: gitRootDir });
145
218
  const lines = stdout.split('\n').filter(Boolean);
146
219
 
@@ -158,7 +231,7 @@ async function getGitChangedCsx(projectRoot) {
158
231
  .filter(file => {
159
232
  // Only .csx files that exist and are in our project
160
233
  return file.endsWith('.csx') &&
161
- require('fs').existsSync(file) &&
234
+ fsSync.existsSync(file) &&
162
235
  file.startsWith(path.normalize(projectRoot));
163
236
  });
164
237
 
@@ -169,18 +242,19 @@ async function getGitChangedCsx(projectRoot) {
169
242
  }
170
243
 
171
244
  /**
172
- * Tüm CSX dosyalarını bulur
245
+ * Finds all CSX files in discovered components ONLY
246
+ * Does NOT scan folders outside of paths definition
247
+ * @param {string} projectRoot - Project root folder
248
+ * @returns {Promise<string[]>} CSX file paths
173
249
  */
174
250
  async function findAllCsx(projectRoot) {
175
- const pattern = path.join(projectRoot, '**', 'src', '**', '*.csx');
176
- const files = await glob(pattern, {
177
- ignore: ['**/node_modules/**', '**/dist/**']
178
- });
179
- return files;
251
+ const { findAllCsxInComponents } = require('./discover');
252
+ return findAllCsxInComponents(projectRoot);
180
253
  }
181
254
 
182
255
  module.exports = {
183
256
  encodeToBase64,
257
+ readNativeContent,
184
258
  findJsonFilesForCsx,
185
259
  getCsxLocation,
186
260
  updateCodeInJson,
@@ -188,4 +262,3 @@ module.exports = {
188
262
  getGitChangedCsx,
189
263
  findAllCsx
190
264
  };
191
-
@@ -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
+ };