arcvision 0.2.25 → 0.2.27

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.
@@ -16,7 +16,7 @@ class CLIValidator {
16
16
  if (!fs.existsSync(filePath)) return { valid: false, error: `File not found: ${filePath}` };
17
17
  return { valid: true };
18
18
  },
19
-
19
+
20
20
  fileReadable: (filePath) => {
21
21
  try {
22
22
  fs.accessSync(filePath, fs.constants.R_OK);
@@ -25,7 +25,7 @@ class CLIValidator {
25
25
  return { valid: false, error: `Permission denied: Cannot read ${filePath}` };
26
26
  }
27
27
  },
28
-
28
+
29
29
  fileWritable: (filePath) => {
30
30
  try {
31
31
  const dir = path.dirname(filePath);
@@ -35,7 +35,7 @@ class CLIValidator {
35
35
  return { valid: false, error: `Permission denied: Cannot write to ${filePath}` };
36
36
  }
37
37
  },
38
-
38
+
39
39
  // JSON validations
40
40
  validJSON: (content, source = 'input') => {
41
41
  try {
@@ -45,7 +45,7 @@ class CLIValidator {
45
45
  return { valid: false, error: `Invalid JSON in ${source}: ${error.message}` };
46
46
  }
47
47
  },
48
-
48
+
49
49
  // Directory validations
50
50
  directoryExists: (dirPath) => {
51
51
  if (!dirPath) return { valid: false, error: 'Directory path is required' };
@@ -59,11 +59,11 @@ class CLIValidator {
59
59
  return { valid: false, error: `Directory not accessible: ${dirPath}` };
60
60
  }
61
61
  },
62
-
62
+
63
63
  // Context structure validations
64
64
  validContextStructure: (context) => {
65
65
  const errors = [];
66
-
66
+
67
67
  // Required top-level fields
68
68
  const requiredFields = ['nodes', 'edges', 'system'];
69
69
  requiredFields.forEach(field => {
@@ -71,17 +71,17 @@ class CLIValidator {
71
71
  errors.push(`Missing required field: ${field}`);
72
72
  }
73
73
  });
74
-
74
+
75
75
  // Validate nodes array
76
76
  if (context.nodes && !Array.isArray(context.nodes)) {
77
77
  errors.push('nodes must be an array');
78
78
  }
79
-
79
+
80
80
  // Validate edges array
81
81
  if (context.edges && !Array.isArray(context.edges)) {
82
82
  errors.push('edges must be an array');
83
83
  }
84
-
84
+
85
85
  // Validate system object structure
86
86
  if (context.system) {
87
87
  if (typeof context.system !== 'object' || context.system === null) {
@@ -96,19 +96,19 @@ class CLIValidator {
96
96
  });
97
97
  }
98
98
  }
99
-
99
+
100
100
  return {
101
101
  valid: errors.length === 0,
102
102
  errors
103
103
  };
104
104
  },
105
-
105
+
106
106
  // Invariant validations
107
107
  validInvariants: (invariants) => {
108
108
  if (!Array.isArray(invariants)) {
109
109
  return { valid: false, error: 'Invariants must be an array' };
110
110
  }
111
-
111
+
112
112
  const errors = [];
113
113
  invariants.forEach((invariant, index) => {
114
114
  if (!invariant.id) {
@@ -130,7 +130,7 @@ class CLIValidator {
130
130
  errors.push(`Invariant ${invariant.id || index}: Missing required 'rule' field`);
131
131
  }
132
132
  });
133
-
133
+
134
134
  return {
135
135
  valid: errors.length === 0,
136
136
  errors
@@ -138,14 +138,14 @@ class CLIValidator {
138
138
  }
139
139
  };
140
140
  }
141
-
141
+
142
142
  /**
143
143
  * Comprehensive validation for CLI operations
144
144
  */
145
145
  validateOperation(operation, params) {
146
146
  const errors = [];
147
147
  const warnings = [];
148
-
148
+
149
149
  switch (operation) {
150
150
  case 'scan':
151
151
  this.validateScan(params, errors, warnings);
@@ -162,7 +162,7 @@ class CLIValidator {
162
162
  default:
163
163
  errors.push(`Unknown operation: ${operation}`);
164
164
  }
165
-
165
+
166
166
  return {
167
167
  valid: errors.length === 0,
168
168
  errors,
@@ -170,33 +170,33 @@ class CLIValidator {
170
170
  isValid: errors.length === 0
171
171
  };
172
172
  }
173
-
173
+
174
174
  validateScan(params, errors, warnings) {
175
175
  const { directory } = params;
176
-
176
+
177
177
  // Validate directory
178
178
  const dirValidation = this.validationRules.directoryExists(directory);
179
179
  if (!dirValidation.valid) {
180
180
  errors.push(dirValidation.error);
181
181
  return; // No point continuing if directory doesn't exist
182
182
  }
183
-
183
+
184
184
  // Check for common issues
185
185
  const commonIssues = this.checkCommonScanIssues(directory);
186
186
  warnings.push(...commonIssues.warnings);
187
187
  errors.push(...commonIssues.errors);
188
188
  }
189
-
189
+
190
190
  validateEvaluate(params, errors, warnings) {
191
191
  const { contextFile, invariantsFile, directory } = params;
192
-
192
+
193
193
  // Validate directory
194
194
  const dirValidation = this.validationRules.directoryExists(directory);
195
195
  if (!dirValidation.valid) {
196
196
  errors.push(dirValidation.error);
197
197
  return;
198
198
  }
199
-
199
+
200
200
  // Validate context file
201
201
  if (contextFile) {
202
202
  const ctxFileValidation = this.validationRules.fileExists(contextFile);
@@ -224,13 +224,11 @@ class CLIValidator {
224
224
  }
225
225
  }
226
226
  }
227
-
227
+
228
228
  // Validate invariants file if provided
229
229
  if (invariantsFile) {
230
230
  const invFileValidation = this.validationRules.fileExists(invariantsFile);
231
- if (!invFileValidation.valid) {
232
- warnings.push(`Invariants file not found: ${invariantsFile} (will use defaults)`);
233
- } else {
231
+ if (invFileValidation.valid) {
234
232
  const readable = this.validationRules.fileReadable(invariantsFile);
235
233
  if (!readable.valid) {
236
234
  warnings.push(`Cannot read invariants file: ${readable.error} (will use defaults)`);
@@ -254,17 +252,17 @@ class CLIValidator {
254
252
  }
255
253
  }
256
254
  }
257
-
255
+
258
256
  validateUpload(params, errors, warnings) {
259
257
  const { contextData, token } = params;
260
-
258
+
261
259
  // Validate token
262
260
  if (!token) {
263
261
  errors.push('Upload token is required. Run `arcvision link <TOKEN>` first.');
264
262
  } else if (typeof token !== 'string' || token.length < 10) {
265
263
  warnings.push('Upload token appears to be invalid or malformed.');
266
264
  }
267
-
265
+
268
266
  // Validate context data
269
267
  if (!contextData) {
270
268
  errors.push('Context data is required for upload');
@@ -273,7 +271,7 @@ class CLIValidator {
273
271
  if (!structureValidation.valid) {
274
272
  errors.push(...structureValidation.errors.map(e => `Context validation: ${e}`));
275
273
  }
276
-
274
+
277
275
  // Check data size
278
276
  try {
279
277
  const dataSize = Buffer.byteLength(JSON.stringify(contextData));
@@ -286,26 +284,26 @@ class CLIValidator {
286
284
  }
287
285
  }
288
286
  }
289
-
287
+
290
288
  validateDiff(params, errors, warnings) {
291
289
  const { oldFile, newFile } = params;
292
-
290
+
293
291
  // Validate both files exist and are readable
294
292
  [oldFile, newFile].forEach((file, index) => {
295
293
  const label = index === 0 ? 'Old file' : 'New file';
296
-
294
+
297
295
  const fileValidation = this.validationRules.fileExists(file);
298
296
  if (!fileValidation.valid) {
299
297
  errors.push(`${label}: ${fileValidation.error}`);
300
298
  return;
301
299
  }
302
-
300
+
303
301
  const readable = this.validationRules.fileReadable(file);
304
302
  if (!readable.valid) {
305
303
  errors.push(`${label}: ${readable.error}`);
306
304
  return;
307
305
  }
308
-
306
+
309
307
  try {
310
308
  const content = fs.readFileSync(file, 'utf8');
311
309
  const jsonValidation = this.validationRules.validJSON(content, file);
@@ -321,13 +319,13 @@ class CLIValidator {
321
319
  errors.push(`${label}: Failed to read file - ${error.message}`);
322
320
  }
323
321
  });
324
-
322
+
325
323
  // Check if files are different
326
324
  if (oldFile === newFile) {
327
325
  warnings.push('Both files are the same. Diff will show no changes.');
328
326
  }
329
327
  }
330
-
328
+
331
329
  /**
332
330
  * Extract invariants from various JSON structures
333
331
  */
@@ -335,15 +333,15 @@ class CLIValidator {
335
333
  if (Array.isArray(data)) {
336
334
  return data;
337
335
  }
338
-
336
+
339
337
  if (data.project_specific_invariants && Array.isArray(data.project_specific_invariants)) {
340
338
  return data.project_specific_invariants;
341
339
  }
342
-
340
+
343
341
  if (data.invariants && Array.isArray(data.invariants)) {
344
342
  return data.invariants;
345
343
  }
346
-
344
+
347
345
  // Try to find invariants in any property
348
346
  const keys = Object.keys(data);
349
347
  for (const key of keys) {
@@ -351,54 +349,54 @@ class CLIValidator {
351
349
  return data[key];
352
350
  }
353
351
  }
354
-
352
+
355
353
  return [];
356
354
  }
357
-
355
+
358
356
  /**
359
357
  * Check for common scan issues
360
358
  */
361
359
  checkCommonScanIssues(directory) {
362
360
  const warnings = [];
363
361
  const errors = [];
364
-
362
+
365
363
  try {
366
364
  // Check for node_modules
367
365
  const nodeModulesPath = path.join(directory, 'node_modules');
368
366
  if (fs.existsSync(nodeModulesPath)) {
369
367
  warnings.push('Found node_modules directory. Consider adding it to ignore patterns for better performance.');
370
368
  }
371
-
369
+
372
370
  // Check for large directories
373
371
  const entries = fs.readdirSync(directory, { withFileTypes: true });
374
372
  const fileCount = entries.filter(entry => entry.isFile()).length;
375
373
  const dirCount = entries.filter(entry => entry.isDirectory()).length;
376
-
374
+
377
375
  if (fileCount > 1000) {
378
376
  warnings.push(`Large number of files detected (${fileCount}). Scan may take longer than usual.`);
379
377
  }
380
-
378
+
381
379
  if (dirCount > 100) {
382
380
  warnings.push(`Large number of directories detected (${dirCount}). Consider specifying a subdirectory.`);
383
381
  }
384
-
382
+
385
383
  // Check for common problematic files
386
384
  const problematicExtensions = ['.log', '.tmp', '.cache'];
387
- const hasProblematicFiles = entries.some(entry =>
385
+ const hasProblematicFiles = entries.some(entry =>
388
386
  entry.isFile() && problematicExtensions.some(ext => entry.name.endsWith(ext))
389
387
  );
390
-
388
+
391
389
  if (hasProblematicFiles) {
392
390
  warnings.push('Found potentially problematic files (.log, .tmp, .cache). These will be skipped during scan.');
393
391
  }
394
-
392
+
395
393
  } catch (error) {
396
394
  errors.push(`Could not analyze directory structure: ${error.message}`);
397
395
  }
398
-
396
+
399
397
  return { warnings, errors };
400
398
  }
401
-
399
+
402
400
  /**
403
401
  * Format validation results for display
404
402
  */
@@ -406,40 +404,40 @@ class CLIValidator {
406
404
  if (results.isValid) {
407
405
  return chalk.green('✅ All validations passed');
408
406
  }
409
-
407
+
410
408
  let output = '';
411
-
409
+
412
410
  if (results.errors.length > 0) {
413
411
  output += chalk.red('❌ Validation Errors:\n');
414
412
  results.errors.forEach(error => {
415
413
  output += ` • ${error}\n`;
416
414
  });
417
415
  }
418
-
416
+
419
417
  if (results.warnings.length > 0) {
420
418
  output += chalk.yellow('\n⚠️ Validation Warnings:\n');
421
419
  results.warnings.forEach(warning => {
422
420
  output += ` • ${warning}\n`;
423
421
  });
424
422
  }
425
-
423
+
426
424
  return output;
427
425
  }
428
-
426
+
429
427
  /**
430
428
  * Perform comprehensive pre-flight validation
431
429
  */
432
430
  async preFlightValidation(operation, params) {
433
431
  console.log(chalk.blue(`🔍 Pre-flight validation for ${operation} operation...`));
434
-
432
+
435
433
  const validation = this.validateOperation(operation, params);
436
-
434
+
437
435
  if (!validation.isValid) {
438
436
  console.error(chalk.red('\n❌ Pre-flight validation failed:'));
439
437
  console.error(this.formatValidationResults(validation));
440
438
  process.exit(1);
441
439
  }
442
-
440
+
443
441
  if (validation.warnings.length > 0) {
444
442
  console.warn(chalk.yellow('\n⚠️ Validation warnings:'));
445
443
  validation.warnings.forEach(warning => {
@@ -447,7 +445,7 @@ class CLIValidator {
447
445
  });
448
446
  console.log(); // Add spacing
449
447
  }
450
-
448
+
451
449
  console.log(chalk.green('✅ Pre-flight validation passed\n'));
452
450
  return true;
453
451
  }