lula2 0.4.0 → 0.5.0-nightly.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/dist/_app/immutable/assets/0.Dfpe5goI.css +1 -0
- package/dist/_app/immutable/chunks/{d2wclEM1.js → B3DV5AB9.js} +1 -1
- package/dist/_app/immutable/chunks/{DTNJlR4l.js → BBW9xKix.js} +19 -19
- package/dist/_app/immutable/chunks/{B8sFn9qB.js → BIY1u1I9.js} +1 -1
- package/dist/_app/immutable/chunks/{DPYPcVpy.js → BN4ish10.js} +1 -1
- package/dist/_app/immutable/chunks/{D6AXzSy_.js → BtwnwKFn.js} +1 -1
- package/dist/_app/immutable/chunks/{CgOk0Ct0.js → C2de20AA.js} +3 -3
- package/dist/_app/immutable/chunks/Cyp5c8fY.js +1 -0
- package/dist/_app/immutable/chunks/{DL7cUWpq.js → D6NghQtU.js} +1 -1
- package/dist/_app/immutable/chunks/{AMsv4DTs.js → DBN1r830.js} +1 -1
- package/dist/_app/immutable/entry/{app.DCeR7c2L.js → app.ChNLcnhL.js} +2 -2
- package/dist/_app/immutable/entry/start.CuWGVoVi.js +1 -0
- package/dist/_app/immutable/nodes/0.DnmE0r6A.js +2 -0
- package/dist/_app/immutable/nodes/{1.DZuIkAUv.js → 1.CPF_x4ZW.js} +1 -1
- package/dist/_app/immutable/nodes/{2.CaMZrOwr.js → 2.dJ7_0KZr.js} +1 -1
- package/dist/_app/immutable/nodes/{3.BCB0DxLi.js → 3.BhR2ddch.js} +1 -1
- package/dist/_app/immutable/nodes/{4.BlOrY_m9.js → 4.Djg06sYy.js} +1 -1
- package/dist/_app/version.json +1 -1
- package/dist/cli/commands/ui.js +265 -49
- package/dist/cli/server/index.js +265 -49
- package/dist/cli/server/server.js +265 -49
- package/dist/cli/server/spreadsheetRoutes.js +282 -59
- package/dist/cli/server/websocketServer.js +265 -49
- package/dist/index.html +10 -10
- package/dist/index.js +265 -49
- package/package.json +22 -23
- package/src/lib/components/dialogs/ExportColumnDialog.svelte +163 -0
- package/src/lib/components/dialogs/index.ts +4 -0
- package/src/routes/+layout.svelte +67 -5
- package/src/routes/control/[id]/+page.svelte +0 -1
- package/dist/_app/immutable/assets/0.D6CB7gA7.css +0 -1
- package/dist/_app/immutable/chunks/CBRQrpza.js +0 -1
- package/dist/_app/immutable/entry/start.Ca2qbK08.js +0 -1
- package/dist/_app/immutable/nodes/0.B2WQMmWy.js +0 -1
|
@@ -1414,7 +1414,10 @@ function extractFamilyFromControlId(controlId) {
|
|
|
1414
1414
|
}
|
|
1415
1415
|
return controlId.substring(0, 2).toUpperCase();
|
|
1416
1416
|
}
|
|
1417
|
-
function exportAsCSV(controls, metadata, res) {
|
|
1417
|
+
function exportAsCSV(controls, metadata, mappingsColumn, res) {
|
|
1418
|
+
return exportAsCSVWithMapping(controls, metadata, { mappings: mappingsColumn }, res);
|
|
1419
|
+
}
|
|
1420
|
+
function exportAsCSVWithMapping(controls, metadata, columnMappings, res) {
|
|
1418
1421
|
const fieldSchema = metadata?.fieldSchema?.fields || {};
|
|
1419
1422
|
const controlIdField = metadata?.control_id_field || "id";
|
|
1420
1423
|
const allFields = /* @__PURE__ */ new Set();
|
|
@@ -1422,49 +1425,91 @@ function exportAsCSV(controls, metadata, res) {
|
|
|
1422
1425
|
Object.keys(control).forEach((key) => allFields.add(key));
|
|
1423
1426
|
});
|
|
1424
1427
|
const fieldMapping = [];
|
|
1428
|
+
const usedDisplayNames = /* @__PURE__ */ new Set();
|
|
1425
1429
|
if (allFields.has(controlIdField)) {
|
|
1426
1430
|
const idSchema = fieldSchema[controlIdField];
|
|
1427
|
-
|
|
1428
|
-
|
|
1429
|
-
|
|
1430
|
-
|
|
1431
|
+
const displayName = idSchema?.original_name || "Control ID";
|
|
1432
|
+
let isMappingColumn = false;
|
|
1433
|
+
if (columnMappings["mappings"]) {
|
|
1434
|
+
const targetDisplayName = columnMappings["mappings"];
|
|
1435
|
+
if (displayName.toLowerCase() === targetDisplayName.toLowerCase()) {
|
|
1436
|
+
isMappingColumn = true;
|
|
1437
|
+
}
|
|
1438
|
+
}
|
|
1439
|
+
fieldMapping.push({ fieldName: controlIdField, displayName, isMappingColumn });
|
|
1440
|
+
usedDisplayNames.add(displayName.toLowerCase());
|
|
1431
1441
|
allFields.delete(controlIdField);
|
|
1432
1442
|
} else if (allFields.has("id")) {
|
|
1433
|
-
|
|
1434
|
-
|
|
1435
|
-
|
|
1436
|
-
|
|
1443
|
+
let isMappingColumn = false;
|
|
1444
|
+
const displayName = "Control ID";
|
|
1445
|
+
if (columnMappings["mappings"]) {
|
|
1446
|
+
const targetDisplayName = columnMappings["mappings"];
|
|
1447
|
+
if (displayName.toLowerCase() === targetDisplayName.toLowerCase()) {
|
|
1448
|
+
isMappingColumn = true;
|
|
1449
|
+
}
|
|
1450
|
+
}
|
|
1451
|
+
fieldMapping.push({ fieldName: "id", displayName, isMappingColumn });
|
|
1452
|
+
usedDisplayNames.add("control id");
|
|
1437
1453
|
allFields.delete("id");
|
|
1438
1454
|
}
|
|
1439
1455
|
if (allFields.has("family")) {
|
|
1440
1456
|
const familySchema = fieldSchema["family"];
|
|
1441
|
-
|
|
1442
|
-
|
|
1443
|
-
|
|
1444
|
-
});
|
|
1457
|
+
const displayName = familySchema?.original_name || "Family";
|
|
1458
|
+
fieldMapping.push({ fieldName: "family", displayName });
|
|
1459
|
+
usedDisplayNames.add(displayName.toLowerCase());
|
|
1445
1460
|
allFields.delete("family");
|
|
1446
1461
|
}
|
|
1447
1462
|
Array.from(allFields).filter((field) => field !== "mappings" && field !== "mappings_count").sort().forEach((field) => {
|
|
1448
1463
|
const schema = fieldSchema[field];
|
|
1449
|
-
const
|
|
1450
|
-
|
|
1464
|
+
const defaultDisplayName = schema?.original_name || field.replace(/-/g, " ").replace(/\b\w/g, (l) => l.toUpperCase());
|
|
1465
|
+
let isMappingColumn = false;
|
|
1466
|
+
let finalDisplayName = defaultDisplayName;
|
|
1467
|
+
if (columnMappings["mappings"]) {
|
|
1468
|
+
const targetDisplayName = columnMappings["mappings"];
|
|
1469
|
+
if (defaultDisplayName.toLowerCase() === targetDisplayName.toLowerCase()) {
|
|
1470
|
+
isMappingColumn = true;
|
|
1471
|
+
finalDisplayName = targetDisplayName;
|
|
1472
|
+
}
|
|
1473
|
+
}
|
|
1474
|
+
if (!usedDisplayNames.has(finalDisplayName.toLowerCase())) {
|
|
1475
|
+
fieldMapping.push({
|
|
1476
|
+
fieldName: field,
|
|
1477
|
+
displayName: finalDisplayName,
|
|
1478
|
+
isMappingColumn
|
|
1479
|
+
});
|
|
1480
|
+
usedDisplayNames.add(finalDisplayName.toLowerCase());
|
|
1481
|
+
}
|
|
1451
1482
|
});
|
|
1452
|
-
if (allFields.has("
|
|
1453
|
-
fieldMapping.push({ fieldName: "mappings_count", displayName: "Mappings Count" });
|
|
1454
|
-
}
|
|
1455
|
-
if (allFields.has("mappings")) {
|
|
1483
|
+
if (allFields.has("mappings") && (!columnMappings["mappings"] || columnMappings["mappings"] === "Mappings")) {
|
|
1456
1484
|
fieldMapping.push({ fieldName: "mappings", displayName: "Mappings" });
|
|
1457
1485
|
}
|
|
1458
1486
|
const csvRows = [];
|
|
1459
1487
|
csvRows.push(fieldMapping.map((field) => `"${field.displayName}"`).join(","));
|
|
1460
1488
|
controls.forEach((control) => {
|
|
1461
|
-
const row = fieldMapping.map(({ fieldName }) => {
|
|
1462
|
-
|
|
1489
|
+
const row = fieldMapping.map(({ fieldName, isMappingColumn }) => {
|
|
1490
|
+
let value;
|
|
1491
|
+
if (isMappingColumn) {
|
|
1492
|
+
const mappingsValue = control["mappings"];
|
|
1493
|
+
if (Array.isArray(mappingsValue) && mappingsValue.length > 0) {
|
|
1494
|
+
const mappingsStr = mappingsValue.map((m) => m.description || m.justification || "").filter((desc) => desc && desc.trim() !== "").join("\n");
|
|
1495
|
+
if (mappingsStr.trim() !== "") {
|
|
1496
|
+
value = mappingsStr;
|
|
1497
|
+
} else {
|
|
1498
|
+
value = control[fieldName];
|
|
1499
|
+
}
|
|
1500
|
+
} else {
|
|
1501
|
+
value = control[fieldName];
|
|
1502
|
+
}
|
|
1503
|
+
} else {
|
|
1504
|
+
value = control[fieldName];
|
|
1505
|
+
}
|
|
1463
1506
|
if (value === void 0 || value === null) return '""';
|
|
1464
1507
|
if (fieldName === "mappings" && Array.isArray(value)) {
|
|
1465
|
-
const mappingsStr = value.map(
|
|
1466
|
-
|
|
1467
|
-
|
|
1508
|
+
const mappingsStr = value.map((m) => {
|
|
1509
|
+
const justification = m.description || m.justification || "";
|
|
1510
|
+
const status = m.status || "Unknown";
|
|
1511
|
+
return justification.trim() !== "" ? justification : `[${status}]`;
|
|
1512
|
+
}).join("\n");
|
|
1468
1513
|
return `"${mappingsStr.replace(/"/g, '""')}"`;
|
|
1469
1514
|
}
|
|
1470
1515
|
if (Array.isArray(value)) return `"${value.join("; ").replace(/"/g, '""')}"`;
|
|
@@ -1479,32 +1524,101 @@ function exportAsCSV(controls, metadata, res) {
|
|
|
1479
1524
|
res.setHeader("Content-Disposition", `attachment; filename="${fileName}"`);
|
|
1480
1525
|
res.send(csvContent);
|
|
1481
1526
|
}
|
|
1482
|
-
async function exportAsExcel(controls, metadata, res) {
|
|
1527
|
+
async function exportAsExcel(controls, metadata, mappingsColumn, res) {
|
|
1528
|
+
return await exportAsExcelWithMapping(controls, metadata, { mappings: mappingsColumn }, res);
|
|
1529
|
+
}
|
|
1530
|
+
async function exportAsExcelWithMapping(controls, metadata, columnMappings, res) {
|
|
1483
1531
|
const fieldSchema = metadata?.fieldSchema?.fields || {};
|
|
1484
1532
|
const controlIdField = metadata?.control_id_field || "id";
|
|
1485
|
-
const
|
|
1486
|
-
|
|
1487
|
-
|
|
1488
|
-
|
|
1489
|
-
|
|
1490
|
-
|
|
1491
|
-
|
|
1492
|
-
|
|
1533
|
+
const allFields = /* @__PURE__ */ new Set();
|
|
1534
|
+
controls.forEach((control) => {
|
|
1535
|
+
Object.keys(control).forEach((key) => allFields.add(key));
|
|
1536
|
+
});
|
|
1537
|
+
const fieldMapping = [];
|
|
1538
|
+
const usedDisplayNames = /* @__PURE__ */ new Set();
|
|
1539
|
+
if (allFields.has(controlIdField)) {
|
|
1540
|
+
const idSchema = fieldSchema[controlIdField];
|
|
1541
|
+
const displayName = idSchema?.original_name || "Control ID";
|
|
1542
|
+
let isMappingColumn = false;
|
|
1543
|
+
if (columnMappings["mappings"]) {
|
|
1544
|
+
const targetDisplayName = columnMappings["mappings"];
|
|
1545
|
+
if (displayName.toLowerCase() === targetDisplayName.toLowerCase()) {
|
|
1546
|
+
isMappingColumn = true;
|
|
1547
|
+
}
|
|
1493
1548
|
}
|
|
1494
|
-
|
|
1495
|
-
|
|
1496
|
-
|
|
1497
|
-
|
|
1549
|
+
fieldMapping.push({ fieldName: controlIdField, displayName, isMappingColumn });
|
|
1550
|
+
usedDisplayNames.add(displayName.toLowerCase());
|
|
1551
|
+
allFields.delete(controlIdField);
|
|
1552
|
+
} else if (allFields.has("id")) {
|
|
1553
|
+
let isMappingColumn = false;
|
|
1554
|
+
const displayName = "Control ID";
|
|
1555
|
+
if (columnMappings["mappings"]) {
|
|
1556
|
+
const targetDisplayName = columnMappings["mappings"];
|
|
1557
|
+
if (displayName.toLowerCase() === targetDisplayName.toLowerCase()) {
|
|
1558
|
+
isMappingColumn = true;
|
|
1559
|
+
}
|
|
1560
|
+
}
|
|
1561
|
+
fieldMapping.push({ fieldName: "id", displayName, isMappingColumn });
|
|
1562
|
+
usedDisplayNames.add("control id");
|
|
1563
|
+
allFields.delete("id");
|
|
1564
|
+
}
|
|
1565
|
+
if (allFields.has("family")) {
|
|
1566
|
+
const familySchema = fieldSchema["family"];
|
|
1567
|
+
const displayName = familySchema?.original_name || "Family";
|
|
1568
|
+
fieldMapping.push({ fieldName: "family", displayName });
|
|
1569
|
+
usedDisplayNames.add(displayName.toLowerCase());
|
|
1570
|
+
allFields.delete("family");
|
|
1571
|
+
}
|
|
1572
|
+
Array.from(allFields).filter((field) => field !== "mappings" && field !== "mappings_count").sort().forEach((field) => {
|
|
1573
|
+
const schema = fieldSchema[field];
|
|
1574
|
+
const defaultDisplayName = schema?.original_name || field.replace(/-/g, " ").replace(/\b\w/g, (l) => l.toUpperCase());
|
|
1575
|
+
let isMappingColumn = false;
|
|
1576
|
+
let finalDisplayName = defaultDisplayName;
|
|
1577
|
+
if (columnMappings["mappings"]) {
|
|
1578
|
+
const targetDisplayName = columnMappings["mappings"];
|
|
1579
|
+
if (defaultDisplayName.toLowerCase() === targetDisplayName.toLowerCase()) {
|
|
1580
|
+
isMappingColumn = true;
|
|
1581
|
+
finalDisplayName = targetDisplayName;
|
|
1582
|
+
}
|
|
1583
|
+
}
|
|
1584
|
+
if (!usedDisplayNames.has(finalDisplayName.toLowerCase())) {
|
|
1585
|
+
fieldMapping.push({
|
|
1586
|
+
fieldName: field,
|
|
1587
|
+
displayName: finalDisplayName,
|
|
1588
|
+
isMappingColumn
|
|
1589
|
+
});
|
|
1590
|
+
usedDisplayNames.add(finalDisplayName.toLowerCase());
|
|
1498
1591
|
}
|
|
1499
|
-
|
|
1500
|
-
|
|
1501
|
-
|
|
1502
|
-
|
|
1503
|
-
|
|
1504
|
-
|
|
1505
|
-
|
|
1506
|
-
|
|
1507
|
-
|
|
1592
|
+
});
|
|
1593
|
+
if (allFields.has("mappings") && (!columnMappings["mappings"] || columnMappings["mappings"] === "Mappings")) {
|
|
1594
|
+
fieldMapping.push({ fieldName: "mappings", displayName: "Mappings" });
|
|
1595
|
+
}
|
|
1596
|
+
const worksheetData = controls.map((control) => {
|
|
1597
|
+
const exportControl = {};
|
|
1598
|
+
fieldMapping.forEach(({ fieldName, displayName, isMappingColumn }) => {
|
|
1599
|
+
let value;
|
|
1600
|
+
if (isMappingColumn) {
|
|
1601
|
+
const mappingsValue = control["mappings"];
|
|
1602
|
+
if (Array.isArray(mappingsValue) && mappingsValue.length > 0) {
|
|
1603
|
+
const mappingsStr = mappingsValue.map((m) => m.description || m.justification || "").filter((desc) => desc && desc.trim() !== "").join("\n");
|
|
1604
|
+
if (mappingsStr.trim() !== "") {
|
|
1605
|
+
value = mappingsStr;
|
|
1606
|
+
} else {
|
|
1607
|
+
value = control[fieldName];
|
|
1608
|
+
}
|
|
1609
|
+
} else {
|
|
1610
|
+
value = control[fieldName];
|
|
1611
|
+
}
|
|
1612
|
+
} else {
|
|
1613
|
+
value = control[fieldName];
|
|
1614
|
+
}
|
|
1615
|
+
if (fieldName === "mappings" && Array.isArray(value)) {
|
|
1616
|
+
const mappingsStr = value.map((m) => {
|
|
1617
|
+
const justification = m.description || m.justification || "";
|
|
1618
|
+
const status = m.status || "Unknown";
|
|
1619
|
+
return justification.trim() !== "" ? justification : `[${status}]`;
|
|
1620
|
+
}).join("\n");
|
|
1621
|
+
exportControl[displayName] = mappingsStr;
|
|
1508
1622
|
} else if (Array.isArray(value)) {
|
|
1509
1623
|
exportControl[displayName] = value.join("; ");
|
|
1510
1624
|
} else if (typeof value === "object" && value !== null) {
|
|
@@ -1918,6 +2032,7 @@ var init_spreadsheetRoutes = __esm({
|
|
|
1918
2032
|
router.get("/export-controls", async (req, res) => {
|
|
1919
2033
|
try {
|
|
1920
2034
|
const format = req.query.format || "csv";
|
|
2035
|
+
const mappingsColumn = req.query.mappingsColumn || "Mappings";
|
|
1921
2036
|
const state = getServerState();
|
|
1922
2037
|
const fileStore = state.fileStore;
|
|
1923
2038
|
if (!fileStore) {
|
|
@@ -1927,7 +2042,8 @@ var init_spreadsheetRoutes = __esm({
|
|
|
1927
2042
|
const mappings = await fileStore.loadMappings();
|
|
1928
2043
|
let metadata = {};
|
|
1929
2044
|
try {
|
|
1930
|
-
const
|
|
2045
|
+
const controlSetPath = getCurrentControlSetPath();
|
|
2046
|
+
const metadataPath2 = join4(controlSetPath, "lula.yaml");
|
|
1931
2047
|
if (existsSync3(metadataPath2)) {
|
|
1932
2048
|
const metadataContent = readFileSync3(metadataPath2, "utf8");
|
|
1933
2049
|
metadata = yaml4.load(metadataContent);
|
|
@@ -1955,10 +2071,10 @@ var init_spreadsheetRoutes = __esm({
|
|
|
1955
2071
|
debug(`Exporting ${controlsWithMappings.length} controls as ${format}`);
|
|
1956
2072
|
switch (format.toLowerCase()) {
|
|
1957
2073
|
case "csv":
|
|
1958
|
-
return exportAsCSV(controlsWithMappings, metadata, res);
|
|
2074
|
+
return exportAsCSV(controlsWithMappings, metadata, mappingsColumn, res);
|
|
1959
2075
|
case "excel":
|
|
1960
2076
|
case "xlsx":
|
|
1961
|
-
return await exportAsExcel(controlsWithMappings, metadata, res);
|
|
2077
|
+
return await exportAsExcel(controlsWithMappings, metadata, mappingsColumn, res);
|
|
1962
2078
|
case "json":
|
|
1963
2079
|
return exportAsJSON(controlsWithMappings, metadata, res);
|
|
1964
2080
|
default:
|
|
@@ -1969,6 +2085,106 @@ var init_spreadsheetRoutes = __esm({
|
|
|
1969
2085
|
res.status(500).json({ error: error.message });
|
|
1970
2086
|
}
|
|
1971
2087
|
});
|
|
2088
|
+
router.get("/export-column-headers", async (req, res) => {
|
|
2089
|
+
try {
|
|
2090
|
+
let metadata = {};
|
|
2091
|
+
try {
|
|
2092
|
+
const controlSetPath = getCurrentControlSetPath();
|
|
2093
|
+
const metadataPath2 = join4(controlSetPath, "lula.yaml");
|
|
2094
|
+
if (existsSync3(metadataPath2)) {
|
|
2095
|
+
const metadataContent = readFileSync3(metadataPath2, "utf8");
|
|
2096
|
+
metadata = yaml4.load(metadataContent);
|
|
2097
|
+
} else {
|
|
2098
|
+
return res.status(404).json({ error: `No lula.yaml file found in control set path: ${controlSetPath}` });
|
|
2099
|
+
}
|
|
2100
|
+
} catch {
|
|
2101
|
+
return res.status(500).json({ error: "Failed to read lula.yaml file" });
|
|
2102
|
+
}
|
|
2103
|
+
const fieldSchema = metadata?.fieldSchema?.fields || {};
|
|
2104
|
+
const controlIdField = metadata?.control_id_field || "id";
|
|
2105
|
+
const columnHeaders = [];
|
|
2106
|
+
if (fieldSchema[controlIdField]) {
|
|
2107
|
+
const idSchema = fieldSchema[controlIdField];
|
|
2108
|
+
const displayName = idSchema?.original_name || "Control ID";
|
|
2109
|
+
columnHeaders.push({
|
|
2110
|
+
value: displayName,
|
|
2111
|
+
label: displayName
|
|
2112
|
+
});
|
|
2113
|
+
} else {
|
|
2114
|
+
columnHeaders.push({ value: "Control ID", label: "Control ID" });
|
|
2115
|
+
}
|
|
2116
|
+
if (fieldSchema["family"]) {
|
|
2117
|
+
const familySchema = fieldSchema["family"];
|
|
2118
|
+
const displayName = familySchema?.original_name || "Family";
|
|
2119
|
+
columnHeaders.push({
|
|
2120
|
+
value: displayName,
|
|
2121
|
+
label: displayName
|
|
2122
|
+
});
|
|
2123
|
+
}
|
|
2124
|
+
Object.entries(fieldSchema).forEach(([fieldName, schema]) => {
|
|
2125
|
+
if (fieldName === controlIdField || fieldName === "family" || fieldName === "mappings" || fieldName === "mappings_count") {
|
|
2126
|
+
return;
|
|
2127
|
+
}
|
|
2128
|
+
const displayName = schema?.original_name || fieldName.replace(/-/g, " ").replace(/\b\w/g, (l) => l.toUpperCase());
|
|
2129
|
+
columnHeaders.push({
|
|
2130
|
+
value: displayName,
|
|
2131
|
+
label: displayName
|
|
2132
|
+
});
|
|
2133
|
+
});
|
|
2134
|
+
columnHeaders.push({ value: "Mappings", label: "Mappings (Default)" });
|
|
2135
|
+
res.json({
|
|
2136
|
+
columnHeaders,
|
|
2137
|
+
defaultColumn: "Mappings"
|
|
2138
|
+
});
|
|
2139
|
+
} catch (error) {
|
|
2140
|
+
res.status(500).json({ error: error.message });
|
|
2141
|
+
}
|
|
2142
|
+
});
|
|
2143
|
+
router.post("/export-csv", async (req, res) => {
|
|
2144
|
+
try {
|
|
2145
|
+
const { format = "csv", _columns = [], columnMappings = {} } = req.body;
|
|
2146
|
+
const state = getServerState();
|
|
2147
|
+
const fileStore = state.fileStore;
|
|
2148
|
+
if (!fileStore) {
|
|
2149
|
+
return res.status(500).json({ error: "No control set loaded" });
|
|
2150
|
+
}
|
|
2151
|
+
const controls = await fileStore.loadAllControls();
|
|
2152
|
+
const mappings = await fileStore.loadMappings();
|
|
2153
|
+
let metadata = {};
|
|
2154
|
+
try {
|
|
2155
|
+
const controlSetPath = getCurrentControlSetPath();
|
|
2156
|
+
const metadataPath2 = join4(controlSetPath, "lula.yaml");
|
|
2157
|
+
if (existsSync3(metadataPath2)) {
|
|
2158
|
+
const metadataContent = readFileSync3(metadataPath2, "utf8");
|
|
2159
|
+
metadata = yaml4.load(metadataContent);
|
|
2160
|
+
}
|
|
2161
|
+
} catch (err) {
|
|
2162
|
+
debug("Could not load metadata:", err);
|
|
2163
|
+
}
|
|
2164
|
+
if (!controls || controls.length === 0) {
|
|
2165
|
+
return res.status(404).json({ error: "No controls found" });
|
|
2166
|
+
}
|
|
2167
|
+
const controlIdField = metadata?.control_id_field || "id";
|
|
2168
|
+
const controlsWithMappings = controls.map((control) => {
|
|
2169
|
+
const controlId = control[controlIdField] || control.id;
|
|
2170
|
+
const controlMappings = mappings.filter((m) => m.control_id === controlId);
|
|
2171
|
+
return {
|
|
2172
|
+
...control,
|
|
2173
|
+
mappings_count: controlMappings.length,
|
|
2174
|
+
mappings: controlMappings.map((m) => ({
|
|
2175
|
+
uuid: m.uuid,
|
|
2176
|
+
status: m.status,
|
|
2177
|
+
description: m.justification || ""
|
|
2178
|
+
}))
|
|
2179
|
+
};
|
|
2180
|
+
});
|
|
2181
|
+
debug(`Exporting ${controlsWithMappings.length} controls as ${format} with column mappings`);
|
|
2182
|
+
return exportAsCSVWithMapping(controlsWithMappings, metadata, columnMappings, res);
|
|
2183
|
+
} catch (error) {
|
|
2184
|
+
console.error("Export error:", error);
|
|
2185
|
+
res.status(500).json({ error: error.message });
|
|
2186
|
+
}
|
|
2187
|
+
});
|
|
1972
2188
|
router.post("/parse-excel", upload.single("file"), async (req, res) => {
|
|
1973
2189
|
try {
|
|
1974
2190
|
if (!req.file) {
|
package/dist/index.html
CHANGED
|
@@ -6,28 +6,28 @@
|
|
|
6
6
|
<link rel="icon" href="/lula.png" />
|
|
7
7
|
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
|
8
8
|
|
|
9
|
-
<link rel="modulepreload" href="/_app/immutable/entry/start.
|
|
10
|
-
<link rel="modulepreload" href="/_app/immutable/chunks/
|
|
11
|
-
<link rel="modulepreload" href="/_app/immutable/chunks/
|
|
12
|
-
<link rel="modulepreload" href="/_app/immutable/entry/app.
|
|
9
|
+
<link rel="modulepreload" href="/_app/immutable/entry/start.CuWGVoVi.js">
|
|
10
|
+
<link rel="modulepreload" href="/_app/immutable/chunks/C2de20AA.js">
|
|
11
|
+
<link rel="modulepreload" href="/_app/immutable/chunks/BIY1u1I9.js">
|
|
12
|
+
<link rel="modulepreload" href="/_app/immutable/entry/app.ChNLcnhL.js">
|
|
13
13
|
<link rel="modulepreload" href="/_app/immutable/chunks/DsnmJJEf.js">
|
|
14
|
-
<link rel="modulepreload" href="/_app/immutable/chunks/
|
|
15
|
-
<link rel="modulepreload" href="/_app/immutable/chunks/
|
|
16
|
-
<link rel="modulepreload" href="/_app/immutable/chunks/
|
|
14
|
+
<link rel="modulepreload" href="/_app/immutable/chunks/DBN1r830.js">
|
|
15
|
+
<link rel="modulepreload" href="/_app/immutable/chunks/D6NghQtU.js">
|
|
16
|
+
<link rel="modulepreload" href="/_app/immutable/chunks/BN4ish10.js">
|
|
17
17
|
</head>
|
|
18
18
|
<body data-sveltekit-preload-data="hover">
|
|
19
19
|
<div style="display: contents">
|
|
20
20
|
<script>
|
|
21
21
|
{
|
|
22
|
-
|
|
22
|
+
__sveltekit_1wq4dle = {
|
|
23
23
|
base: ""
|
|
24
24
|
};
|
|
25
25
|
|
|
26
26
|
const element = document.currentScript.parentElement;
|
|
27
27
|
|
|
28
28
|
Promise.all([
|
|
29
|
-
import("/_app/immutable/entry/start.
|
|
30
|
-
import("/_app/immutable/entry/app.
|
|
29
|
+
import("/_app/immutable/entry/start.CuWGVoVi.js"),
|
|
30
|
+
import("/_app/immutable/entry/app.ChNLcnhL.js")
|
|
31
31
|
]).then(([kit, app]) => {
|
|
32
32
|
kit.start(app, element);
|
|
33
33
|
});
|