devbonzai 1.8.0 → 1.8.1
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 +69 -1
- package/cli.js +401 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -13,4 +13,72 @@ npm publish
|
|
|
13
13
|
- node cli.js
|
|
14
14
|
- this will setup a receiver in this directory
|
|
15
15
|
|
|
16
|
-
Used for Bonzai's linking functionality from web to local development environment.
|
|
16
|
+
Used for Bonzai's linking functionality from web to local development environment.
|
|
17
|
+
|
|
18
|
+
## API Endpoints
|
|
19
|
+
|
|
20
|
+
The server exposes several endpoints for file operations and code analysis:
|
|
21
|
+
|
|
22
|
+
### Import Validation
|
|
23
|
+
|
|
24
|
+
**POST /validate-imports** - Validate imports in a single file
|
|
25
|
+
|
|
26
|
+
Request body:
|
|
27
|
+
```json
|
|
28
|
+
{
|
|
29
|
+
"filePath": "src/components/Button.js",
|
|
30
|
+
"imports": [
|
|
31
|
+
{
|
|
32
|
+
"path": "./utils",
|
|
33
|
+
"line": 5,
|
|
34
|
+
"symbols": [
|
|
35
|
+
{ "name": "formatDate", "isDefault": false },
|
|
36
|
+
{ "name": "default", "isDefault": true }
|
|
37
|
+
]
|
|
38
|
+
}
|
|
39
|
+
]
|
|
40
|
+
}
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
Response:
|
|
44
|
+
```json
|
|
45
|
+
{
|
|
46
|
+
"file": "src/components/Button.js",
|
|
47
|
+
"errors": [
|
|
48
|
+
{
|
|
49
|
+
"line": 5,
|
|
50
|
+
"import": "./utils",
|
|
51
|
+
"symbol": "formatDate",
|
|
52
|
+
"error": "'formatDate' is not exported from './utils'"
|
|
53
|
+
}
|
|
54
|
+
],
|
|
55
|
+
"errorCount": 1
|
|
56
|
+
}
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
**POST /validate-imports-batch** - Validate imports for multiple files
|
|
60
|
+
|
|
61
|
+
Request body:
|
|
62
|
+
```json
|
|
63
|
+
{
|
|
64
|
+
"files": [
|
|
65
|
+
{
|
|
66
|
+
"filePath": "src/components/Button.js",
|
|
67
|
+
"imports": [...]
|
|
68
|
+
},
|
|
69
|
+
{
|
|
70
|
+
"filePath": "src/components/Input.js",
|
|
71
|
+
"imports": [...]
|
|
72
|
+
}
|
|
73
|
+
]
|
|
74
|
+
}
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
Response: Array of validation results (same format as single file endpoint)
|
|
78
|
+
|
|
79
|
+
### Supported Features
|
|
80
|
+
|
|
81
|
+
- **JavaScript/TypeScript**: Validates default exports, named exports, and namespace imports
|
|
82
|
+
- **Python**: Validates function, class, and variable imports
|
|
83
|
+
- **File Resolution**: Automatically resolves relative imports and checks for files with common extensions (.js, .jsx, .ts, .tsx, .py, index files)
|
|
84
|
+
- **External Packages**: Skips validation for node_modules imports
|
package/cli.js
CHANGED
|
@@ -92,6 +92,8 @@ app.get('/', (req, res) => {
|
|
|
92
92
|
'POST /move': 'Move file or folder (body: {source, destination})',
|
|
93
93
|
'POST /open-cursor': 'Open Cursor (body: {path, line?})',
|
|
94
94
|
'POST /prompt_agent': 'Execute cursor-agent command (body: {prompt})',
|
|
95
|
+
'POST /validate-imports': 'Validate imports in a file (body: {filePath, imports})',
|
|
96
|
+
'POST /validate-imports-batch': 'Validate imports for multiple files (body: {files})',
|
|
95
97
|
'POST /shutdown': 'Gracefully shutdown the server'
|
|
96
98
|
},
|
|
97
99
|
example: 'Try: /list or /read?path=README.md'
|
|
@@ -1294,6 +1296,405 @@ app.post('/prompt_agent', (req, res) => {
|
|
|
1294
1296
|
});
|
|
1295
1297
|
});
|
|
1296
1298
|
|
|
1299
|
+
// Helper function to resolve import path relative to source file
|
|
1300
|
+
function resolveImportPath(importPath, sourceFilePath) {
|
|
1301
|
+
// Remove root folder name if present
|
|
1302
|
+
let relativeSourcePath = sourceFilePath;
|
|
1303
|
+
const rootName = path.basename(ROOT);
|
|
1304
|
+
if (relativeSourcePath.startsWith(rootName + '/')) {
|
|
1305
|
+
relativeSourcePath = relativeSourcePath.substring(rootName.length + 1);
|
|
1306
|
+
}
|
|
1307
|
+
|
|
1308
|
+
const sourceDir = path.dirname(path.join(ROOT, relativeSourcePath));
|
|
1309
|
+
|
|
1310
|
+
// Handle relative imports
|
|
1311
|
+
if (importPath.startsWith('./') || importPath.startsWith('../')) {
|
|
1312
|
+
const resolved = path.resolve(sourceDir, importPath);
|
|
1313
|
+
return path.relative(ROOT, resolved).replace(/\\\\/g, '/');
|
|
1314
|
+
}
|
|
1315
|
+
|
|
1316
|
+
// Handle absolute imports (node_modules, etc.)
|
|
1317
|
+
// For now, just return as-is - we'll check node_modules separately
|
|
1318
|
+
return importPath;
|
|
1319
|
+
}
|
|
1320
|
+
|
|
1321
|
+
// Helper function to check if a file exists (with various extensions)
|
|
1322
|
+
function findFileWithExtensions(basePath) {
|
|
1323
|
+
const extensions = ['', '.js', '.jsx', '.ts', '.tsx', '.py', '/index.js', '/index.ts', '/index.tsx'];
|
|
1324
|
+
const rootName = path.basename(ROOT);
|
|
1325
|
+
|
|
1326
|
+
for (const ext of extensions) {
|
|
1327
|
+
const testPath = basePath + ext;
|
|
1328
|
+
|
|
1329
|
+
// Remove root folder name if present
|
|
1330
|
+
let relativePath = testPath;
|
|
1331
|
+
if (relativePath.startsWith(rootName + '/')) {
|
|
1332
|
+
relativePath = relativePath.substring(rootName.length + 1);
|
|
1333
|
+
}
|
|
1334
|
+
const actualPath = path.join(ROOT, relativePath);
|
|
1335
|
+
|
|
1336
|
+
if (fs.existsSync(actualPath) && fs.statSync(actualPath).isFile()) {
|
|
1337
|
+
return relativePath;
|
|
1338
|
+
}
|
|
1339
|
+
}
|
|
1340
|
+
|
|
1341
|
+
return null;
|
|
1342
|
+
}
|
|
1343
|
+
|
|
1344
|
+
// Helper function to extract exports from JavaScript/TypeScript file
|
|
1345
|
+
function getJavaScriptExports(filePath) {
|
|
1346
|
+
try {
|
|
1347
|
+
if (!babelParser) {
|
|
1348
|
+
return { default: false, named: [], all: false };
|
|
1349
|
+
}
|
|
1350
|
+
|
|
1351
|
+
const content = fs.readFileSync(filePath, 'utf8');
|
|
1352
|
+
const isTypeScript = filePath.endsWith('.ts') || filePath.endsWith('.tsx');
|
|
1353
|
+
|
|
1354
|
+
const ast = babelParser.parse(content, {
|
|
1355
|
+
sourceType: 'module',
|
|
1356
|
+
plugins: [
|
|
1357
|
+
'typescript',
|
|
1358
|
+
'jsx',
|
|
1359
|
+
'decorators-legacy',
|
|
1360
|
+
'classProperties',
|
|
1361
|
+
'objectRestSpread',
|
|
1362
|
+
'asyncGenerators',
|
|
1363
|
+
'functionBind',
|
|
1364
|
+
'exportDefaultFrom',
|
|
1365
|
+
'exportNamespaceFrom',
|
|
1366
|
+
'dynamicImport',
|
|
1367
|
+
'nullishCoalescingOperator',
|
|
1368
|
+
'optionalChaining'
|
|
1369
|
+
]
|
|
1370
|
+
});
|
|
1371
|
+
|
|
1372
|
+
const exports = { default: false, named: [], all: false };
|
|
1373
|
+
|
|
1374
|
+
function traverse(node) {
|
|
1375
|
+
if (!node) return;
|
|
1376
|
+
|
|
1377
|
+
// export default
|
|
1378
|
+
if (node.type === 'ExportDefaultDeclaration') {
|
|
1379
|
+
exports.default = true;
|
|
1380
|
+
}
|
|
1381
|
+
|
|
1382
|
+
// export { A, B }
|
|
1383
|
+
if (node.type === 'ExportNamedDeclaration') {
|
|
1384
|
+
if (node.specifiers) {
|
|
1385
|
+
node.specifiers.forEach(spec => {
|
|
1386
|
+
if (spec.type === 'ExportSpecifier') {
|
|
1387
|
+
const name = spec.exported.name || spec.exported.value;
|
|
1388
|
+
if (name === '*') {
|
|
1389
|
+
exports.all = true;
|
|
1390
|
+
} else {
|
|
1391
|
+
exports.named.push(name);
|
|
1392
|
+
}
|
|
1393
|
+
}
|
|
1394
|
+
});
|
|
1395
|
+
}
|
|
1396
|
+
// export function/class
|
|
1397
|
+
if (node.declaration) {
|
|
1398
|
+
if (node.declaration.type === 'FunctionDeclaration' && node.declaration.id) {
|
|
1399
|
+
exports.named.push(node.declaration.id.name);
|
|
1400
|
+
} else if (node.declaration.type === 'ClassDeclaration' && node.declaration.id) {
|
|
1401
|
+
exports.named.push(node.declaration.id.name);
|
|
1402
|
+
}
|
|
1403
|
+
}
|
|
1404
|
+
}
|
|
1405
|
+
|
|
1406
|
+
// export * from './file'
|
|
1407
|
+
if (node.type === 'ExportAllDeclaration') {
|
|
1408
|
+
exports.all = true;
|
|
1409
|
+
}
|
|
1410
|
+
|
|
1411
|
+
// Recursively traverse
|
|
1412
|
+
for (const key in node) {
|
|
1413
|
+
if (key === 'parent' || key === 'leadingComments' || key === 'trailingComments') continue;
|
|
1414
|
+
const child = node[key];
|
|
1415
|
+
if (Array.isArray(child)) {
|
|
1416
|
+
child.forEach(c => traverse(c));
|
|
1417
|
+
} else if (child && typeof child === 'object' && child.type) {
|
|
1418
|
+
traverse(child);
|
|
1419
|
+
}
|
|
1420
|
+
}
|
|
1421
|
+
}
|
|
1422
|
+
|
|
1423
|
+
traverse(ast);
|
|
1424
|
+
return exports;
|
|
1425
|
+
} catch (e) {
|
|
1426
|
+
// If parsing fails, return empty exports
|
|
1427
|
+
return { default: false, named: [], all: false };
|
|
1428
|
+
}
|
|
1429
|
+
}
|
|
1430
|
+
|
|
1431
|
+
// Helper function to check if symbol exists in Python file
|
|
1432
|
+
function checkPythonSymbol(filePath, symbolName) {
|
|
1433
|
+
try {
|
|
1434
|
+
const content = fs.readFileSync(filePath, 'utf8');
|
|
1435
|
+
const lines = content.split('\\n');
|
|
1436
|
+
|
|
1437
|
+
// Check for function definitions
|
|
1438
|
+
const funcPattern = new RegExp(\`^\\\\s*def\\\\s+\${symbolName}\\\\s*\\\\(\`);
|
|
1439
|
+
// Check for class definitions
|
|
1440
|
+
const classPattern = new RegExp(\`^\\\\s*class\\\\s+\${symbolName}\\\\s*[(:]\`);
|
|
1441
|
+
// Check for variable assignments
|
|
1442
|
+
const varPattern = new RegExp(\`^\\\\s*\${symbolName}\\\\s*=\`);
|
|
1443
|
+
|
|
1444
|
+
for (const line of lines) {
|
|
1445
|
+
if (funcPattern.test(line) || classPattern.test(line) || varPattern.test(line)) {
|
|
1446
|
+
return true;
|
|
1447
|
+
}
|
|
1448
|
+
}
|
|
1449
|
+
|
|
1450
|
+
// Check __all__ if present
|
|
1451
|
+
const allMatch = content.match(/^__all__\\s*=\\s*\\[(.*?)\\]/m);
|
|
1452
|
+
if (allMatch) {
|
|
1453
|
+
const allExports = allMatch[1].split(',').map(s => s.trim().replace(/['"]/g, ''));
|
|
1454
|
+
return allExports.includes(symbolName);
|
|
1455
|
+
}
|
|
1456
|
+
|
|
1457
|
+
return false;
|
|
1458
|
+
} catch (e) {
|
|
1459
|
+
return false;
|
|
1460
|
+
}
|
|
1461
|
+
}
|
|
1462
|
+
|
|
1463
|
+
// Validate imports for a single file
|
|
1464
|
+
app.post('/validate-imports', (req, res) => {
|
|
1465
|
+
try {
|
|
1466
|
+
const { filePath, imports } = req.body;
|
|
1467
|
+
|
|
1468
|
+
if (!filePath || !imports || !Array.isArray(imports)) {
|
|
1469
|
+
return res.status(400).json({ error: 'filePath and imports array required' });
|
|
1470
|
+
}
|
|
1471
|
+
|
|
1472
|
+
const errors = [];
|
|
1473
|
+
const rootName = path.basename(ROOT);
|
|
1474
|
+
|
|
1475
|
+
// Remove root folder name if present
|
|
1476
|
+
let relativeFilePath = filePath;
|
|
1477
|
+
if (relativeFilePath.startsWith(rootName + '/')) {
|
|
1478
|
+
relativeFilePath = relativeFilePath.substring(rootName.length + 1);
|
|
1479
|
+
}
|
|
1480
|
+
|
|
1481
|
+
for (const imp of imports) {
|
|
1482
|
+
const importPath = imp.path;
|
|
1483
|
+
const symbols = imp.symbols || [];
|
|
1484
|
+
|
|
1485
|
+
// Resolve import path
|
|
1486
|
+
const resolvedPath = resolveImportPath(importPath, filePath);
|
|
1487
|
+
|
|
1488
|
+
// Check if file exists
|
|
1489
|
+
const foundFile = findFileWithExtensions(resolvedPath);
|
|
1490
|
+
|
|
1491
|
+
if (!foundFile) {
|
|
1492
|
+
// Check if it's a node_modules import (skip validation for external packages)
|
|
1493
|
+
if (!importPath.startsWith('./') && !importPath.startsWith('../') && !importPath.startsWith('/')) {
|
|
1494
|
+
// Likely a node_modules import, skip
|
|
1495
|
+
continue;
|
|
1496
|
+
}
|
|
1497
|
+
|
|
1498
|
+
errors.push({
|
|
1499
|
+
line: imp.line,
|
|
1500
|
+
import: importPath,
|
|
1501
|
+
error: \`Cannot find module '\${importPath}'\`
|
|
1502
|
+
});
|
|
1503
|
+
continue;
|
|
1504
|
+
}
|
|
1505
|
+
|
|
1506
|
+
// Check exports for each symbol
|
|
1507
|
+
const fullFilePath = path.join(ROOT, foundFile);
|
|
1508
|
+
const extension = foundFile.split('.').pop()?.toLowerCase();
|
|
1509
|
+
|
|
1510
|
+
if (['js', 'jsx', 'ts', 'tsx'].includes(extension)) {
|
|
1511
|
+
const exports = getJavaScriptExports(fullFilePath);
|
|
1512
|
+
|
|
1513
|
+
for (const symbol of symbols) {
|
|
1514
|
+
if (symbol.isNamespace || symbol.name === '*') {
|
|
1515
|
+
// Namespace import - check if file has any exports
|
|
1516
|
+
if (!exports.default && exports.named.length === 0 && !exports.all) {
|
|
1517
|
+
errors.push({
|
|
1518
|
+
line: imp.line,
|
|
1519
|
+
import: importPath,
|
|
1520
|
+
symbol: symbol.name,
|
|
1521
|
+
error: \`Module '\${importPath}' has no exports\`
|
|
1522
|
+
});
|
|
1523
|
+
}
|
|
1524
|
+
} else if (symbol.isDefault) {
|
|
1525
|
+
if (!exports.default) {
|
|
1526
|
+
errors.push({
|
|
1527
|
+
line: imp.line,
|
|
1528
|
+
import: importPath,
|
|
1529
|
+
symbol: symbol.name,
|
|
1530
|
+
error: \`'\${symbol.name}' is not exported as default from '\${importPath}'\`
|
|
1531
|
+
});
|
|
1532
|
+
}
|
|
1533
|
+
} else {
|
|
1534
|
+
// Named export
|
|
1535
|
+
if (!exports.named.includes(symbol.name) && !exports.all) {
|
|
1536
|
+
errors.push({
|
|
1537
|
+
line: imp.line,
|
|
1538
|
+
import: importPath,
|
|
1539
|
+
symbol: symbol.name,
|
|
1540
|
+
error: \`'\${symbol.name}' is not exported from '\${importPath}'\`
|
|
1541
|
+
});
|
|
1542
|
+
}
|
|
1543
|
+
}
|
|
1544
|
+
}
|
|
1545
|
+
} else if (extension === 'py') {
|
|
1546
|
+
// Python validation
|
|
1547
|
+
for (const symbol of symbols) {
|
|
1548
|
+
if (symbol.name === '*' || symbol.isNamespace) {
|
|
1549
|
+
// Wildcard import - just check if file exists (already done)
|
|
1550
|
+
continue;
|
|
1551
|
+
}
|
|
1552
|
+
|
|
1553
|
+
// Check if symbol exists in Python file
|
|
1554
|
+
if (!checkPythonSymbol(fullFilePath, symbol.name)) {
|
|
1555
|
+
errors.push({
|
|
1556
|
+
line: imp.line,
|
|
1557
|
+
import: importPath,
|
|
1558
|
+
symbol: symbol.name,
|
|
1559
|
+
error: \`'\${symbol.name}' is not defined in '\${importPath}'\`
|
|
1560
|
+
});
|
|
1561
|
+
}
|
|
1562
|
+
}
|
|
1563
|
+
}
|
|
1564
|
+
}
|
|
1565
|
+
|
|
1566
|
+
res.json({
|
|
1567
|
+
file: filePath,
|
|
1568
|
+
errors,
|
|
1569
|
+
errorCount: errors.length
|
|
1570
|
+
});
|
|
1571
|
+
} catch (e) {
|
|
1572
|
+
console.error('Error validating imports:', e);
|
|
1573
|
+
res.status(500).json({ error: e.message });
|
|
1574
|
+
}
|
|
1575
|
+
});
|
|
1576
|
+
|
|
1577
|
+
// Validate imports for multiple files (batch)
|
|
1578
|
+
app.post('/validate-imports-batch', (req, res) => {
|
|
1579
|
+
try {
|
|
1580
|
+
const { files } = req.body;
|
|
1581
|
+
|
|
1582
|
+
if (!files || !Array.isArray(files)) {
|
|
1583
|
+
return res.status(400).json({ error: 'files array required' });
|
|
1584
|
+
}
|
|
1585
|
+
|
|
1586
|
+
const results = [];
|
|
1587
|
+
|
|
1588
|
+
for (const file of files) {
|
|
1589
|
+
const { filePath, imports } = file;
|
|
1590
|
+
|
|
1591
|
+
if (!filePath || !imports || !Array.isArray(imports)) {
|
|
1592
|
+
results.push({ file: filePath || 'unknown', errors: [], errorCount: 0 });
|
|
1593
|
+
continue;
|
|
1594
|
+
}
|
|
1595
|
+
|
|
1596
|
+
const errors = [];
|
|
1597
|
+
const rootName = path.basename(ROOT);
|
|
1598
|
+
|
|
1599
|
+
// Remove root folder name if present
|
|
1600
|
+
let relativeFilePath = filePath;
|
|
1601
|
+
if (relativeFilePath.startsWith(rootName + '/')) {
|
|
1602
|
+
relativeFilePath = relativeFilePath.substring(rootName.length + 1);
|
|
1603
|
+
}
|
|
1604
|
+
|
|
1605
|
+
for (const imp of imports) {
|
|
1606
|
+
const importPath = imp.path;
|
|
1607
|
+
const symbols = imp.symbols || [];
|
|
1608
|
+
|
|
1609
|
+
// Resolve import path
|
|
1610
|
+
const resolvedPath = resolveImportPath(importPath, filePath);
|
|
1611
|
+
|
|
1612
|
+
// Check if file exists
|
|
1613
|
+
const foundFile = findFileWithExtensions(resolvedPath);
|
|
1614
|
+
|
|
1615
|
+
if (!foundFile) {
|
|
1616
|
+
// Check if it's a node_modules import
|
|
1617
|
+
if (!importPath.startsWith('./') && !importPath.startsWith('../') && !importPath.startsWith('/')) {
|
|
1618
|
+
continue;
|
|
1619
|
+
}
|
|
1620
|
+
|
|
1621
|
+
errors.push({
|
|
1622
|
+
line: imp.line,
|
|
1623
|
+
import: importPath,
|
|
1624
|
+
error: \`Cannot find module '\${importPath}'\`
|
|
1625
|
+
});
|
|
1626
|
+
continue;
|
|
1627
|
+
}
|
|
1628
|
+
|
|
1629
|
+
// Check exports
|
|
1630
|
+
const fullFilePath = path.join(ROOT, foundFile);
|
|
1631
|
+
const extension = foundFile.split('.').pop()?.toLowerCase();
|
|
1632
|
+
|
|
1633
|
+
if (['js', 'jsx', 'ts', 'tsx'].includes(extension)) {
|
|
1634
|
+
const exports = getJavaScriptExports(fullFilePath);
|
|
1635
|
+
|
|
1636
|
+
for (const symbol of symbols) {
|
|
1637
|
+
if (symbol.isNamespace || symbol.name === '*') {
|
|
1638
|
+
if (!exports.default && exports.named.length === 0 && !exports.all) {
|
|
1639
|
+
errors.push({
|
|
1640
|
+
line: imp.line,
|
|
1641
|
+
import: importPath,
|
|
1642
|
+
symbol: symbol.name,
|
|
1643
|
+
error: \`Module '\${importPath}' has no exports\`
|
|
1644
|
+
});
|
|
1645
|
+
}
|
|
1646
|
+
} else if (symbol.isDefault) {
|
|
1647
|
+
if (!exports.default) {
|
|
1648
|
+
errors.push({
|
|
1649
|
+
line: imp.line,
|
|
1650
|
+
import: importPath,
|
|
1651
|
+
symbol: symbol.name,
|
|
1652
|
+
error: \`'\${symbol.name}' is not exported as default from '\${importPath}'\`
|
|
1653
|
+
});
|
|
1654
|
+
}
|
|
1655
|
+
} else {
|
|
1656
|
+
if (!exports.named.includes(symbol.name) && !exports.all) {
|
|
1657
|
+
errors.push({
|
|
1658
|
+
line: imp.line,
|
|
1659
|
+
import: importPath,
|
|
1660
|
+
symbol: symbol.name,
|
|
1661
|
+
error: \`'\${symbol.name}' is not exported from '\${importPath}'\`
|
|
1662
|
+
});
|
|
1663
|
+
}
|
|
1664
|
+
}
|
|
1665
|
+
}
|
|
1666
|
+
} else if (extension === 'py') {
|
|
1667
|
+
for (const symbol of symbols) {
|
|
1668
|
+
if (symbol.name === '*' || symbol.isNamespace) {
|
|
1669
|
+
continue;
|
|
1670
|
+
}
|
|
1671
|
+
|
|
1672
|
+
if (!checkPythonSymbol(fullFilePath, symbol.name)) {
|
|
1673
|
+
errors.push({
|
|
1674
|
+
line: imp.line,
|
|
1675
|
+
import: importPath,
|
|
1676
|
+
symbol: symbol.name,
|
|
1677
|
+
error: \`'\${symbol.name}' is not defined in '\${importPath}'\`
|
|
1678
|
+
});
|
|
1679
|
+
}
|
|
1680
|
+
}
|
|
1681
|
+
}
|
|
1682
|
+
}
|
|
1683
|
+
|
|
1684
|
+
results.push({
|
|
1685
|
+
file: filePath,
|
|
1686
|
+
errors,
|
|
1687
|
+
errorCount: errors.length
|
|
1688
|
+
});
|
|
1689
|
+
}
|
|
1690
|
+
|
|
1691
|
+
res.json(results);
|
|
1692
|
+
} catch (e) {
|
|
1693
|
+
console.error('Error validating imports batch:', e);
|
|
1694
|
+
res.status(500).json({ error: e.message });
|
|
1695
|
+
}
|
|
1696
|
+
});
|
|
1697
|
+
|
|
1297
1698
|
// Shutdown endpoint to kill the server
|
|
1298
1699
|
app.post('/shutdown', (req, res) => {
|
|
1299
1700
|
console.log('🛑 Shutdown endpoint called - terminating server...');
|