@aifabrix/builder 2.6.2 → 2.7.0

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.
@@ -0,0 +1,299 @@
1
+ /**
2
+ * Main Validation Command
3
+ *
4
+ * Validates applications or external integration files.
5
+ * Supports app name validation (including externalIntegration block) or direct file validation.
6
+ *
7
+ * @fileoverview Main validation command for AI Fabrix Builder
8
+ * @author AI Fabrix Team
9
+ * @version 2.0.0
10
+ */
11
+
12
+ const fs = require('fs');
13
+ const path = require('path');
14
+ const chalk = require('chalk');
15
+ const validator = require('./validator');
16
+ const { resolveExternalFiles } = require('./utils/schema-resolver');
17
+ const { loadExternalSystemSchema, loadExternalDataSourceSchema, detectSchemaType } = require('./utils/schema-loader');
18
+ const { formatValidationErrors } = require('./utils/error-formatter');
19
+ const logger = require('./utils/logger');
20
+
21
+ /**
22
+ * Validates a single external file against its schema
23
+ *
24
+ * @async
25
+ * @function validateExternalFile
26
+ * @param {string} filePath - Path to the file
27
+ * @param {string} type - File type: 'system' | 'datasource'
28
+ * @returns {Promise<Object>} Validation result
29
+ */
30
+ async function validateExternalFile(filePath, type) {
31
+ if (!fs.existsSync(filePath)) {
32
+ throw new Error(`File not found: ${filePath}`);
33
+ }
34
+
35
+ const content = fs.readFileSync(filePath, 'utf8');
36
+ let parsed;
37
+
38
+ try {
39
+ parsed = JSON.parse(content);
40
+ } catch (error) {
41
+ return {
42
+ valid: false,
43
+ errors: [`Invalid JSON syntax: ${error.message}`],
44
+ warnings: []
45
+ };
46
+ }
47
+
48
+ let validate;
49
+ if (type === 'system') {
50
+ validate = loadExternalSystemSchema();
51
+ } else if (type === 'datasource') {
52
+ validate = loadExternalDataSourceSchema();
53
+ } else {
54
+ throw new Error(`Unknown file type: ${type}`);
55
+ }
56
+
57
+ const valid = validate(parsed);
58
+
59
+ return {
60
+ valid,
61
+ errors: valid ? [] : formatValidationErrors(validate.errors),
62
+ warnings: []
63
+ };
64
+ }
65
+
66
+ /**
67
+ * Validates application or external integration file
68
+ * Detects if input is app name or file path and validates accordingly
69
+ *
70
+ * @async
71
+ * @function validateAppOrFile
72
+ * @param {string} appOrFile - Application name or file path
73
+ * @returns {Promise<Object>} Validation result with aggregated results
74
+ * @throws {Error} If validation fails
75
+ *
76
+ * @example
77
+ * const result = await validateAppOrFile('myapp');
78
+ * // Returns: { valid: true, application: {...}, externalFiles: [...] }
79
+ */
80
+ async function validateAppOrFile(appOrFile) {
81
+ if (!appOrFile || typeof appOrFile !== 'string') {
82
+ throw new Error('App name or file path is required');
83
+ }
84
+
85
+ // Check if it's a file path (exists and is a file)
86
+ const isFilePath = fs.existsSync(appOrFile) && fs.statSync(appOrFile).isFile();
87
+
88
+ if (isFilePath) {
89
+ // Validate single file
90
+ const schemaType = detectSchemaType(appOrFile);
91
+ let result;
92
+
93
+ if (schemaType === 'application') {
94
+ // For application files, we'd need to transform them first
95
+ // For now, just validate JSON structure
96
+ const content = fs.readFileSync(appOrFile, 'utf8');
97
+ try {
98
+ JSON.parse(content);
99
+ } catch (error) {
100
+ return {
101
+ valid: false,
102
+ errors: [`Invalid JSON syntax: ${error.message}`],
103
+ warnings: []
104
+ };
105
+ }
106
+ // Note: Full application validation requires variables.yaml transformation
107
+ // This is a simplified validation for direct JSON files
108
+ result = {
109
+ valid: true,
110
+ errors: [],
111
+ warnings: ['Application file validation is simplified. Use app name for full validation.']
112
+ };
113
+ } else if (schemaType === 'external-system') {
114
+ result = await validateExternalFile(appOrFile, 'system');
115
+ } else if (schemaType === 'external-datasource') {
116
+ result = await validateExternalFile(appOrFile, 'datasource');
117
+ } else {
118
+ throw new Error(`Unknown schema type: ${schemaType}`);
119
+ }
120
+
121
+ return {
122
+ valid: result.valid,
123
+ file: appOrFile,
124
+ type: schemaType,
125
+ errors: result.errors,
126
+ warnings: result.warnings
127
+ };
128
+ }
129
+
130
+ // Treat as app name
131
+ const appName = appOrFile;
132
+
133
+ // Validate application
134
+ const appValidation = await validator.validateApplication(appName);
135
+
136
+ // Check for externalIntegration block
137
+ const variablesPath = path.join(process.cwd(), 'builder', appName, 'variables.yaml');
138
+ if (!fs.existsSync(variablesPath)) {
139
+ return {
140
+ valid: appValidation.valid,
141
+ application: appValidation,
142
+ externalFiles: []
143
+ };
144
+ }
145
+
146
+ const yamlLib = require('js-yaml');
147
+ const content = fs.readFileSync(variablesPath, 'utf8');
148
+ let variables;
149
+
150
+ try {
151
+ variables = yamlLib.load(content);
152
+ } catch (error) {
153
+ return {
154
+ valid: appValidation.valid,
155
+ application: appValidation,
156
+ externalFiles: [],
157
+ warnings: [`Could not parse variables.yaml to check externalIntegration: ${error.message}`]
158
+ };
159
+ }
160
+
161
+ // If no externalIntegration block, return app validation only
162
+ if (!variables.externalIntegration) {
163
+ return {
164
+ valid: appValidation.valid,
165
+ application: appValidation,
166
+ externalFiles: []
167
+ };
168
+ }
169
+
170
+ // Resolve and validate external files
171
+ const externalFiles = await resolveExternalFiles(appName);
172
+ const externalValidations = [];
173
+
174
+ for (const fileInfo of externalFiles) {
175
+ try {
176
+ const validation = await validateExternalFile(fileInfo.path, fileInfo.type);
177
+ externalValidations.push({
178
+ file: fileInfo.fileName,
179
+ path: fileInfo.path,
180
+ type: fileInfo.type,
181
+ ...validation
182
+ });
183
+ } catch (error) {
184
+ externalValidations.push({
185
+ file: fileInfo.fileName,
186
+ path: fileInfo.path,
187
+ type: fileInfo.type,
188
+ valid: false,
189
+ errors: [error.message],
190
+ warnings: []
191
+ });
192
+ }
193
+ }
194
+
195
+ // Aggregate results
196
+ const allValid = appValidation.valid && externalValidations.every(v => v.valid);
197
+ const allErrors = [
198
+ ...appValidation.errors,
199
+ ...externalValidations.flatMap(v => v.errors.map(e => `${v.file}: ${e}`))
200
+ ];
201
+ const allWarnings = [
202
+ ...appValidation.warnings,
203
+ ...externalValidations.flatMap(v => v.warnings)
204
+ ];
205
+
206
+ return {
207
+ valid: allValid,
208
+ application: appValidation,
209
+ externalFiles: externalValidations,
210
+ errors: allErrors,
211
+ warnings: allWarnings
212
+ };
213
+ }
214
+
215
+ /**
216
+ * Displays validation results in a user-friendly format
217
+ *
218
+ * @function displayValidationResults
219
+ * @param {Object} result - Validation result from validateAppOrFile
220
+ */
221
+ function displayValidationResults(result) {
222
+ if (result.valid) {
223
+ logger.log(chalk.green('\n✓ Validation passed!'));
224
+ } else {
225
+ logger.log(chalk.red('\n✗ Validation failed!'));
226
+ }
227
+
228
+ // Display application validation
229
+ if (result.application) {
230
+ logger.log(chalk.blue('\nApplication:'));
231
+ if (result.application.valid) {
232
+ logger.log(chalk.green(' ✓ Application configuration is valid'));
233
+ } else {
234
+ logger.log(chalk.red(' ✗ Application configuration has errors:'));
235
+ result.application.errors.forEach(error => {
236
+ logger.log(chalk.red(` • ${error}`));
237
+ });
238
+ }
239
+ if (result.application.warnings && result.application.warnings.length > 0) {
240
+ result.application.warnings.forEach(warning => {
241
+ logger.log(chalk.yellow(` ⚠ ${warning}`));
242
+ });
243
+ }
244
+ }
245
+
246
+ // Display external files validation
247
+ if (result.externalFiles && result.externalFiles.length > 0) {
248
+ logger.log(chalk.blue('\nExternal Integration Files:'));
249
+ result.externalFiles.forEach(file => {
250
+ if (file.valid) {
251
+ logger.log(chalk.green(` ✓ ${file.file} (${file.type})`));
252
+ } else {
253
+ logger.log(chalk.red(` ✗ ${file.file} (${file.type}):`));
254
+ file.errors.forEach(error => {
255
+ logger.log(chalk.red(` • ${error}`));
256
+ });
257
+ }
258
+ if (file.warnings && file.warnings.length > 0) {
259
+ file.warnings.forEach(warning => {
260
+ logger.log(chalk.yellow(` ⚠ ${warning}`));
261
+ });
262
+ }
263
+ });
264
+ }
265
+
266
+ // Display file validation (for direct file validation)
267
+ if (result.file) {
268
+ logger.log(chalk.blue(`\nFile: ${result.file}`));
269
+ logger.log(chalk.blue(`Type: ${result.type}`));
270
+ if (result.valid) {
271
+ logger.log(chalk.green(' ✓ File is valid'));
272
+ } else {
273
+ logger.log(chalk.red(' ✗ File has errors:'));
274
+ result.errors.forEach(error => {
275
+ logger.log(chalk.red(` • ${error}`));
276
+ });
277
+ }
278
+ if (result.warnings && result.warnings.length > 0) {
279
+ result.warnings.forEach(warning => {
280
+ logger.log(chalk.yellow(` ⚠ ${warning}`));
281
+ });
282
+ }
283
+ }
284
+
285
+ // Display aggregated warnings
286
+ if (result.warnings && result.warnings.length > 0) {
287
+ logger.log(chalk.yellow('\nWarnings:'));
288
+ result.warnings.forEach(warning => {
289
+ logger.log(chalk.yellow(` • ${warning}`));
290
+ });
291
+ }
292
+ }
293
+
294
+ module.exports = {
295
+ validateAppOrFile,
296
+ displayValidationResults,
297
+ validateExternalFile
298
+ };
299
+
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@aifabrix/builder",
3
- "version": "2.6.2",
3
+ "version": "2.7.0",
4
4
  "description": "AI Fabrix Local Fabric & Deployment SDK",
5
5
  "main": "lib/index.js",
6
6
  "bin": {