lula2 0.6.4 → 0.6.5

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.
Files changed (35) hide show
  1. package/README.md +3 -3
  2. package/dist/_app/immutable/assets/0.gNk4bE5Z.css +1 -0
  3. package/dist/_app/immutable/chunks/{DBdrJbLi.js → 6cSSDZaX.js} +1 -1
  4. package/dist/_app/immutable/chunks/{BaV6jU6m.js → BDFhLgJd.js} +1 -1
  5. package/dist/_app/immutable/chunks/{_8PUdHCK.js → BZU_Nz1O.js} +1 -1
  6. package/dist/_app/immutable/chunks/BnySH2DD.js +2 -0
  7. package/dist/_app/immutable/chunks/{sF1amBpw.js → CfxzrOg_.js} +1 -1
  8. package/dist/_app/immutable/chunks/{DG4ZcLIc.js → D2GS6lt_.js} +1 -1
  9. package/dist/_app/immutable/chunks/MY0vM1lA.js +3 -0
  10. package/dist/_app/immutable/chunks/{WNo48c2U.js → Vl8FMUTS.js} +1 -1
  11. package/dist/_app/immutable/chunks/{CynYS-Ma.js → ipJF3Ffx.js} +1 -1
  12. package/dist/_app/immutable/entry/{app.VdrAeFMF.js → app.XAEr8i-M.js} +2 -2
  13. package/dist/_app/immutable/entry/start.DCxCYu1M.js +1 -0
  14. package/dist/_app/immutable/nodes/0.DHn4BGVS.js +2 -0
  15. package/dist/_app/immutable/nodes/{1.Dt4-pegp.js → 1.BRVNZ7Zr.js} +1 -1
  16. package/dist/_app/immutable/nodes/{2.CgJgF2F1.js → 2.DjZVN6-v.js} +1 -1
  17. package/dist/_app/immutable/nodes/{3.DsDKmoyP.js → 3.BwkBTWlm.js} +1 -1
  18. package/dist/_app/immutable/nodes/{4.E9uJ4Np6.js → 4.CrrKXZL6.js} +1 -1
  19. package/dist/_app/version.json +1 -1
  20. package/dist/cli/commands/ui.js +49 -7
  21. package/dist/cli/server/index.js +49 -7
  22. package/dist/cli/server/server.js +49 -7
  23. package/dist/cli/server/serverState.js +37 -4
  24. package/dist/cli/server/spreadsheetRoutes.js +12 -3
  25. package/dist/cli/server/websocketServer.js +49 -7
  26. package/dist/index.html +10 -10
  27. package/dist/index.js +49 -7
  28. package/package.json +126 -126
  29. package/src/lib/components/dialogs/ExportColumnDialog.svelte +7 -5
  30. package/src/routes/+layout.svelte +1 -1
  31. package/dist/_app/immutable/assets/0.CJjXKESY.css +0 -1
  32. package/dist/_app/immutable/chunks/DmBJsPtc.js +0 -2
  33. package/dist/_app/immutable/chunks/DztCq-9O.js +0 -3
  34. package/dist/_app/immutable/entry/start.BD6MZ_XC.js +0 -1
  35. package/dist/_app/immutable/nodes/0.D3GkfY8V.js +0 -2
@@ -150,8 +150,8 @@ var init_fileStore = __esm({
150
150
  if (!this.baseDir || this.baseDir === "." || this.baseDir === process.cwd()) {
151
151
  return;
152
152
  }
153
- const lulaConfigPath = join2(this.baseDir, "lula.yaml");
154
- if (!existsSync2(lulaConfigPath)) {
153
+ const lulaConfigPath2 = join2(this.baseDir, "lula.yaml");
154
+ if (!existsSync2(lulaConfigPath2)) {
155
155
  return;
156
156
  }
157
157
  if (!existsSync2(this.controlsDir)) {
@@ -309,6 +309,17 @@ var init_fileStore = __esm({
309
309
  if (!existsSync2(this.controlsDir)) {
310
310
  return [];
311
311
  }
312
+ let controlOrder = null;
313
+ try {
314
+ const lulaConfigPath2 = join2(this.baseDir, "lula.yaml");
315
+ if (existsSync2(lulaConfigPath2)) {
316
+ const content = readFileSync2(lulaConfigPath2, "utf8");
317
+ const metadata = yaml2.load(content);
318
+ controlOrder = metadata?.controlOrder || null;
319
+ }
320
+ } catch (error) {
321
+ console.error(`Failed to load lula.yaml for controlOrder (path: ${lulaConfigPath}):`, error);
322
+ }
312
323
  const entries = readdirSync(this.controlsDir);
313
324
  const yamlFiles = entries.filter((file) => file.endsWith(".yaml"));
314
325
  if (yamlFiles.length > 0) {
@@ -327,7 +338,11 @@ var init_fileStore = __esm({
327
338
  }
328
339
  });
329
340
  const results2 = await Promise.all(promises);
330
- return results2.filter((c) => c !== null);
341
+ const controls2 = results2.filter((c) => c !== null);
342
+ if (controlOrder && controlOrder.length > 0) {
343
+ return this.sortControlsByOrder(controls2, controlOrder);
344
+ }
345
+ return controls2;
331
346
  }
332
347
  const families = entries.filter((name) => {
333
348
  const familyPath = join2(this.controlsDir, name);
@@ -350,7 +365,25 @@ var init_fileStore = __esm({
350
365
  allPromises.push(...familyPromises);
351
366
  }
352
367
  const results = await Promise.all(allPromises);
353
- return results.filter((c) => c !== null);
368
+ const controls = results.filter((c) => c !== null);
369
+ if (controlOrder && controlOrder.length > 0) {
370
+ return this.sortControlsByOrder(controls, controlOrder);
371
+ }
372
+ return controls;
373
+ }
374
+ /**
375
+ * Sort controls based on the provided order array
376
+ */
377
+ sortControlsByOrder(controls, controlOrder) {
378
+ const orderMap = /* @__PURE__ */ new Map();
379
+ controlOrder.forEach((controlId, index) => {
380
+ orderMap.set(controlId, index);
381
+ });
382
+ return controls.sort((a, b) => {
383
+ const aIndex = orderMap.get(a.id) ?? Number.MAX_SAFE_INTEGER;
384
+ const bIndex = orderMap.get(b.id) ?? Number.MAX_SAFE_INTEGER;
385
+ return aIndex - bIndex;
386
+ });
354
387
  }
355
388
  /**
356
389
  * Load mappings from mappings directory
@@ -1470,6 +1503,7 @@ function processSpreadsheetData(rawData, headers, startRowIndex, params) {
1470
1503
  continue;
1471
1504
  }
1472
1505
  const family = extractFamilyFromControlId(controlId);
1506
+ control._originalRowIndex = i;
1473
1507
  control.family = family;
1474
1508
  controls.push(control);
1475
1509
  if (!families.has(family)) {
@@ -1592,6 +1626,7 @@ async function createOutputStructure(processedData, fieldSchema, params) {
1592
1626
  params.controlIdField,
1593
1627
  params.namingConvention
1594
1628
  );
1629
+ const controlOrder = controls.sort((a, b) => (a._originalRowIndex || 0) - (b._originalRowIndex || 0)).map((control) => control[controlIdFieldNameClean]);
1595
1630
  const controlSetData = {
1596
1631
  name: params.controlSetName,
1597
1632
  description: params.controlSetDescription,
@@ -1599,12 +1634,16 @@ async function createOutputStructure(processedData, fieldSchema, params) {
1599
1634
  control_id_field: controlIdFieldNameClean,
1600
1635
  controlCount: controls.length,
1601
1636
  families: uniqueFamilies,
1637
+ controlOrder,
1602
1638
  fieldSchema
1603
1639
  };
1604
1640
  writeFileSync2(join4(baseDir, "lula.yaml"), yaml4.dump(controlSetData));
1605
1641
  const controlsDir = join4(baseDir, "controls");
1606
1642
  const mappingsDir = join4(baseDir, "mappings");
1607
- families.forEach((familyControls, family) => {
1643
+ const sortedFamilies = Array.from(families.entries()).sort(
1644
+ (a, b) => a[0].localeCompare(b[0])
1645
+ );
1646
+ sortedFamilies.forEach(([family, familyControls]) => {
1608
1647
  const familyDir = join4(controlsDir, family);
1609
1648
  const familyMappingsDir = join4(mappingsDir, family);
1610
1649
  if (!existsSync3(familyDir)) {
@@ -1613,7 +1652,10 @@ async function createOutputStructure(processedData, fieldSchema, params) {
1613
1652
  if (!existsSync3(familyMappingsDir)) {
1614
1653
  mkdirSync2(familyMappingsDir, { recursive: true });
1615
1654
  }
1616
- familyControls.forEach((control) => {
1655
+ const sortedFamilyControls = familyControls.sort(
1656
+ (a, b) => (a._originalRowIndex || 0) - (b._originalRowIndex || 0)
1657
+ );
1658
+ sortedFamilyControls.forEach((control) => {
1617
1659
  const controlId = control[controlIdFieldNameClean];
1618
1660
  if (!controlId) {
1619
1661
  console.error("Missing control ID for control:", control);
@@ -1635,7 +1677,7 @@ async function createOutputStructure(processedData, fieldSchema, params) {
1635
1677
  filteredControl.family = control.family;
1636
1678
  }
1637
1679
  Object.keys(control).forEach((fieldName) => {
1638
- if (fieldName === "family") return;
1680
+ if (fieldName === "family" || fieldName === "_originalRowIndex") return;
1639
1681
  if (params.justificationFields.includes(fieldName) && control[fieldName] !== void 0 && control[fieldName] !== null) {
1640
1682
  justificationContents.push(control[fieldName]);
1641
1683
  }
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.BD6MZ_XC.js">
10
- <link rel="modulepreload" href="/_app/immutable/chunks/DztCq-9O.js">
11
- <link rel="modulepreload" href="/_app/immutable/chunks/DmBJsPtc.js">
12
- <link rel="modulepreload" href="/_app/immutable/entry/app.VdrAeFMF.js">
9
+ <link rel="modulepreload" href="/_app/immutable/entry/start.DCxCYu1M.js">
10
+ <link rel="modulepreload" href="/_app/immutable/chunks/MY0vM1lA.js">
11
+ <link rel="modulepreload" href="/_app/immutable/chunks/BnySH2DD.js">
12
+ <link rel="modulepreload" href="/_app/immutable/entry/app.XAEr8i-M.js">
13
13
  <link rel="modulepreload" href="/_app/immutable/chunks/DsnmJJEf.js">
14
- <link rel="modulepreload" href="/_app/immutable/chunks/BaV6jU6m.js">
15
- <link rel="modulepreload" href="/_app/immutable/chunks/DBdrJbLi.js">
16
- <link rel="modulepreload" href="/_app/immutable/chunks/CynYS-Ma.js">
14
+ <link rel="modulepreload" href="/_app/immutable/chunks/BDFhLgJd.js">
15
+ <link rel="modulepreload" href="/_app/immutable/chunks/6cSSDZaX.js">
16
+ <link rel="modulepreload" href="/_app/immutable/chunks/ipJF3Ffx.js">
17
17
  </head>
18
18
  <body data-sveltekit-preload-data="hover">
19
19
  <div style="display: contents">
20
20
  <script>
21
21
  {
22
- __sveltekit_t1ry3i = {
22
+ __sveltekit_1b7tzml = {
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.BD6MZ_XC.js"),
30
- import("/_app/immutable/entry/app.VdrAeFMF.js")
29
+ import("/_app/immutable/entry/start.DCxCYu1M.js"),
30
+ import("/_app/immutable/entry/app.XAEr8i-M.js")
31
31
  ]).then(([kit, app]) => {
32
32
  kit.start(app, element);
33
33
  });
package/dist/index.js CHANGED
@@ -1722,8 +1722,8 @@ var init_fileStore = __esm({
1722
1722
  if (!this.baseDir || this.baseDir === "." || this.baseDir === process.cwd()) {
1723
1723
  return;
1724
1724
  }
1725
- const lulaConfigPath = join2(this.baseDir, "lula.yaml");
1726
- if (!existsSync2(lulaConfigPath)) {
1725
+ const lulaConfigPath2 = join2(this.baseDir, "lula.yaml");
1726
+ if (!existsSync2(lulaConfigPath2)) {
1727
1727
  return;
1728
1728
  }
1729
1729
  if (!existsSync2(this.controlsDir)) {
@@ -1881,6 +1881,17 @@ var init_fileStore = __esm({
1881
1881
  if (!existsSync2(this.controlsDir)) {
1882
1882
  return [];
1883
1883
  }
1884
+ let controlOrder = null;
1885
+ try {
1886
+ const lulaConfigPath2 = join2(this.baseDir, "lula.yaml");
1887
+ if (existsSync2(lulaConfigPath2)) {
1888
+ const content = readFileSync2(lulaConfigPath2, "utf8");
1889
+ const metadata = yaml2.load(content);
1890
+ controlOrder = metadata?.controlOrder || null;
1891
+ }
1892
+ } catch (error) {
1893
+ console.error(`Failed to load lula.yaml for controlOrder (path: ${lulaConfigPath}):`, error);
1894
+ }
1884
1895
  const entries = readdirSync(this.controlsDir);
1885
1896
  const yamlFiles = entries.filter((file) => file.endsWith(".yaml"));
1886
1897
  if (yamlFiles.length > 0) {
@@ -1899,7 +1910,11 @@ var init_fileStore = __esm({
1899
1910
  }
1900
1911
  });
1901
1912
  const results2 = await Promise.all(promises);
1902
- return results2.filter((c) => c !== null);
1913
+ const controls2 = results2.filter((c) => c !== null);
1914
+ if (controlOrder && controlOrder.length > 0) {
1915
+ return this.sortControlsByOrder(controls2, controlOrder);
1916
+ }
1917
+ return controls2;
1903
1918
  }
1904
1919
  const families = entries.filter((name) => {
1905
1920
  const familyPath = join2(this.controlsDir, name);
@@ -1922,7 +1937,25 @@ var init_fileStore = __esm({
1922
1937
  allPromises.push(...familyPromises);
1923
1938
  }
1924
1939
  const results = await Promise.all(allPromises);
1925
- return results.filter((c) => c !== null);
1940
+ const controls = results.filter((c) => c !== null);
1941
+ if (controlOrder && controlOrder.length > 0) {
1942
+ return this.sortControlsByOrder(controls, controlOrder);
1943
+ }
1944
+ return controls;
1945
+ }
1946
+ /**
1947
+ * Sort controls based on the provided order array
1948
+ */
1949
+ sortControlsByOrder(controls, controlOrder) {
1950
+ const orderMap = /* @__PURE__ */ new Map();
1951
+ controlOrder.forEach((controlId, index) => {
1952
+ orderMap.set(controlId, index);
1953
+ });
1954
+ return controls.sort((a, b) => {
1955
+ const aIndex = orderMap.get(a.id) ?? Number.MAX_SAFE_INTEGER;
1956
+ const bIndex = orderMap.get(b.id) ?? Number.MAX_SAFE_INTEGER;
1957
+ return aIndex - bIndex;
1958
+ });
1926
1959
  }
1927
1960
  /**
1928
1961
  * Load mappings from mappings directory
@@ -3042,6 +3075,7 @@ function processSpreadsheetData(rawData, headers, startRowIndex, params) {
3042
3075
  continue;
3043
3076
  }
3044
3077
  const family = extractFamilyFromControlId(controlId);
3078
+ control._originalRowIndex = i;
3045
3079
  control.family = family;
3046
3080
  controls.push(control);
3047
3081
  if (!families.has(family)) {
@@ -3164,6 +3198,7 @@ async function createOutputStructure(processedData, fieldSchema, params) {
3164
3198
  params.controlIdField,
3165
3199
  params.namingConvention
3166
3200
  );
3201
+ const controlOrder = controls.sort((a, b) => (a._originalRowIndex || 0) - (b._originalRowIndex || 0)).map((control) => control[controlIdFieldNameClean]);
3167
3202
  const controlSetData = {
3168
3203
  name: params.controlSetName,
3169
3204
  description: params.controlSetDescription,
@@ -3171,12 +3206,16 @@ async function createOutputStructure(processedData, fieldSchema, params) {
3171
3206
  control_id_field: controlIdFieldNameClean,
3172
3207
  controlCount: controls.length,
3173
3208
  families: uniqueFamilies,
3209
+ controlOrder,
3174
3210
  fieldSchema
3175
3211
  };
3176
3212
  writeFileSync2(join4(baseDir, "lula.yaml"), yaml4.dump(controlSetData));
3177
3213
  const controlsDir = join4(baseDir, "controls");
3178
3214
  const mappingsDir = join4(baseDir, "mappings");
3179
- families.forEach((familyControls, family) => {
3215
+ const sortedFamilies = Array.from(families.entries()).sort(
3216
+ (a, b) => a[0].localeCompare(b[0])
3217
+ );
3218
+ sortedFamilies.forEach(([family, familyControls]) => {
3180
3219
  const familyDir = join4(controlsDir, family);
3181
3220
  const familyMappingsDir = join4(mappingsDir, family);
3182
3221
  if (!existsSync3(familyDir)) {
@@ -3185,7 +3224,10 @@ async function createOutputStructure(processedData, fieldSchema, params) {
3185
3224
  if (!existsSync3(familyMappingsDir)) {
3186
3225
  mkdirSync2(familyMappingsDir, { recursive: true });
3187
3226
  }
3188
- familyControls.forEach((control) => {
3227
+ const sortedFamilyControls = familyControls.sort(
3228
+ (a, b) => (a._originalRowIndex || 0) - (b._originalRowIndex || 0)
3229
+ );
3230
+ sortedFamilyControls.forEach((control) => {
3189
3231
  const controlId = control[controlIdFieldNameClean];
3190
3232
  if (!controlId) {
3191
3233
  console.error("Missing control ID for control:", control);
@@ -3207,7 +3249,7 @@ async function createOutputStructure(processedData, fieldSchema, params) {
3207
3249
  filteredControl.family = control.family;
3208
3250
  }
3209
3251
  Object.keys(control).forEach((fieldName) => {
3210
- if (fieldName === "family") return;
3252
+ if (fieldName === "family" || fieldName === "_originalRowIndex") return;
3211
3253
  if (params.justificationFields.includes(fieldName) && control[fieldName] !== void 0 && control[fieldName] !== null) {
3212
3254
  justificationContents.push(control[fieldName]);
3213
3255
  }
package/package.json CHANGED
@@ -1,128 +1,128 @@
1
1
  {
2
- "name": "lula2",
3
- "version": "0.6.4",
4
- "description": "A tool for managing compliance as code in your GitHub repositories.",
5
- "bin": {
6
- "lula2": "./dist/lula2"
7
- },
8
- "main": "dist/index.js",
9
- "types": "dist/index.d.ts",
10
- "type": "module",
11
- "engines": {
12
- "node": ">=22.0.0"
13
- },
14
- "repository": {
15
- "type": "git",
16
- "url": "git+https://github.com/defenseunicorns/lula.git"
17
- },
18
- "keywords": [
19
- "compliance",
20
- "devops",
21
- "devsecops"
22
- ],
23
- "author": "Defense Unicorns",
24
- "license": "Apache-2.0",
25
- "bugs": {
26
- "url": "https://github.com/defenseunicorns/lula/issues"
27
- },
28
- "homepage": "https://github.com/defenseunicorns/lula#readme",
29
- "files": [
30
- "/src",
31
- "/dist",
32
- "!src/**/*.test.ts",
33
- "!dist/**/*.test.js*",
34
- "!dist/**/*.test.d.ts*"
35
- ],
36
- "scripts": {
37
- "dev": "vite dev --port 5173",
38
- "dev:api": "tsx --watch index.ts --debug ui --port 3000 --no-open-browser",
39
- "dev:full": "concurrently \"npm run dev:api\" \"npm run dev\"",
40
- "build": "npm run build:svelte && npm run build:cli && npm run postbuild:cli",
41
- "build:svelte": "vite build",
42
- "build:cli": "esbuild index.ts cli/**/*.ts --bundle --platform=node --target=node22 --format=esm --outdir=dist --external:express --external:commander --external:js-yaml --external:yaml --external:isomorphic-git --external:glob --external:open --external:ws --external:cors --external:multer --external:@octokit/rest --external:undici --external:xlsx-republish --external:csv-parse",
43
- "postbuild:cli": "cp cli-wrapper.mjs dist/lula2 && chmod +x dist/lula2",
44
- "preview": "vite preview",
45
- "prepare": "svelte-kit sync || echo ''",
46
- "check": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json && tsc --noEmit",
47
- "check:watch": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json --watch",
48
- "format": "prettier --write 'src/**/*.{ts,js,svelte}' 'cli/**/*.ts' 'index.ts' 'tests/**/*.ts'",
49
- "format:check": "prettier --check 'src/**/*.{ts,js,svelte}' 'cli/**/*.ts' 'index.ts' 'tests/**/*.ts'",
50
- "lint": "prettier --check 'src/**/*.{ts,js,svelte}' 'cli/**/*.ts' 'index.ts' 'tests/**/*.ts' && eslint src cli",
51
- "test": "npm run test:unit -- --run --coverage",
52
- "test:integration": "vitest --config integration/vitest.config.integration.ts",
53
- "test:unit": "vitest"
54
- },
55
- "dependencies": {
56
- "@octokit/rest": "^22.0.0",
57
- "@types/ws": "^8.18.1",
58
- "commander": "^14.0.0",
59
- "cors": "^2.8.5",
60
- "csv-parse": "^6.1.0",
61
- "express": "^5.1.0",
62
- "express-rate-limit": "^8.1.0",
63
- "flowbite": "^3.1.2",
64
- "glob": "^11.0.3",
65
- "isomorphic-git": "^1.33.1",
66
- "js-yaml": "^4.1.0",
67
- "multer": "^2.0.2",
68
- "open": "^10.2.0",
69
- "undici": "^7.15.0",
70
- "ws": "^8.18.3",
71
- "xlsx-republish": "^0.20.3",
72
- "yaml": "^2.8.1"
73
- },
74
- "devDependencies": {
75
- "@commitlint/cli": "^20.0.0",
76
- "@commitlint/config-conventional": "^20.0.0",
77
- "@eslint/compat": "^1.3.2",
78
- "@eslint/eslintrc": "^3.3.1",
79
- "@eslint/js": "^9.35.0",
80
- "@playwright/test": "^1.55.0",
81
- "@sveltejs/adapter-static": "^3.0.9",
82
- "@sveltejs/kit": "^2.37.1",
83
- "@sveltejs/vite-plugin-svelte": "^6.1.4",
84
- "@tailwindcss/vite": "^4.1.13",
85
- "@types/cors": "^2.8.19",
86
- "@types/express": "^5.0.3",
87
- "@types/js-yaml": "^4.0.9",
88
- "@types/jsdom": "^27.0.0",
89
- "@types/multer": "^2.0.0",
90
- "@types/node": "^24.4.0",
91
- "@typescript-eslint/eslint-plugin": "^8.42.0",
92
- "@typescript-eslint/parser": "^8.42.0",
93
- "@vitest/browser": "^3.2.4",
94
- "@vitest/coverage-v8": "^3.2.4",
95
- "carbon-icons-svelte": "^13.5.0",
96
- "carbon-preprocess-svelte": "^0.11.11",
97
- "concurrently": "^9.2.1",
98
- "esbuild": "^0.25.9",
99
- "eslint": "^9.35.0",
100
- "eslint-config-prettier": "^10.1.8",
101
- "eslint-plugin-jsdoc": "^61.0.0",
102
- "eslint-plugin-svelte": "^3.12.2",
103
- "globals": "^16.3.0",
104
- "husky": "^9.1.7",
105
- "jsdom": "^27.0.0",
106
- "playwright": "^1.55.0",
107
- "prettier": "3.6.2",
108
- "prettier-plugin-svelte": "^3.4.0",
109
- "semantic-release": "^24.2.7",
110
- "shellcheck": "^4.1.0",
111
- "svelte": "^5.38.7",
112
- "svelte-check": "^4.3.1",
113
- "tailwind-merge": "^3.3.1",
114
- "tailwindcss": "^4.1.13",
115
- "tsx": "^4.20.5",
116
- "typescript": "5.9.3",
117
- "typescript-eslint": "^8.42.0",
118
- "vite": "^7.1.4",
119
- "vitest": "^3.2.4",
120
- "vitest-browser-svelte": "^1.1.0"
121
- },
122
- "release": {
123
- "branches": [
124
- "main",
125
- "next"
126
- ]
127
- }
2
+ "name": "lula2",
3
+ "version": "0.6.5",
4
+ "description": "A tool for managing compliance as code in your GitHub repositories.",
5
+ "bin": {
6
+ "lula2": "./dist/lula2"
7
+ },
8
+ "main": "dist/index.js",
9
+ "types": "dist/index.d.ts",
10
+ "type": "module",
11
+ "engines": {
12
+ "node": ">=24.0.0"
13
+ },
14
+ "repository": {
15
+ "type": "git",
16
+ "url": "git+https://github.com/defenseunicorns/lula.git"
17
+ },
18
+ "keywords": [
19
+ "compliance",
20
+ "devops",
21
+ "devsecops"
22
+ ],
23
+ "author": "Defense Unicorns",
24
+ "license": "Apache-2.0",
25
+ "bugs": {
26
+ "url": "https://github.com/defenseunicorns/lula/issues"
27
+ },
28
+ "homepage": "https://github.com/defenseunicorns/lula#readme",
29
+ "files": [
30
+ "/src",
31
+ "/dist",
32
+ "!src/**/*.test.ts",
33
+ "!dist/**/*.test.js*",
34
+ "!dist/**/*.test.d.ts*"
35
+ ],
36
+ "scripts": {
37
+ "dev": "vite dev --port 5173",
38
+ "dev:api": "tsx --watch index.ts --debug ui --port 3000 --no-open-browser",
39
+ "dev:full": "concurrently \"npm run dev:api\" \"npm run dev\"",
40
+ "build": "npm run build:svelte && npm run build:cli && npm run postbuild:cli",
41
+ "build:svelte": "vite build",
42
+ "build:cli": "esbuild index.ts cli/**/*.ts --bundle --platform=node --target=node22 --format=esm --outdir=dist --external:express --external:commander --external:js-yaml --external:yaml --external:isomorphic-git --external:glob --external:open --external:ws --external:cors --external:multer --external:@octokit/rest --external:undici --external:xlsx-republish --external:csv-parse",
43
+ "postbuild:cli": "cp cli-wrapper.mjs dist/lula2 && chmod +x dist/lula2",
44
+ "preview": "vite preview",
45
+ "prepare": "svelte-kit sync || echo ''",
46
+ "check": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json && tsc --noEmit",
47
+ "check:watch": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json --watch",
48
+ "format": "prettier --write 'src/**/*.{ts,js,svelte}' 'cli/**/*.ts' 'index.ts' 'tests/**/*.ts'",
49
+ "format:check": "prettier --check 'src/**/*.{ts,js,svelte}' 'cli/**/*.ts' 'index.ts' 'tests/**/*.ts'",
50
+ "lint": "prettier --check 'src/**/*.{ts,js,svelte}' 'cli/**/*.ts' 'index.ts' 'tests/**/*.ts' && eslint src cli",
51
+ "test": "npm run test:unit -- --run --coverage",
52
+ "test:integration": "vitest --config integration/vitest.config.integration.ts",
53
+ "test:unit": "vitest"
54
+ },
55
+ "dependencies": {
56
+ "@octokit/rest": "^22.0.0",
57
+ "@types/ws": "^8.18.1",
58
+ "commander": "^14.0.0",
59
+ "cors": "^2.8.5",
60
+ "csv-parse": "^6.1.0",
61
+ "express": "^5.1.0",
62
+ "express-rate-limit": "^8.1.0",
63
+ "flowbite": "^3.1.2",
64
+ "glob": "^11.0.3",
65
+ "isomorphic-git": "^1.33.1",
66
+ "js-yaml": "^4.1.0",
67
+ "multer": "^2.0.2",
68
+ "open": "^10.2.0",
69
+ "undici": "^7.15.0",
70
+ "ws": "^8.18.3",
71
+ "xlsx-republish": "^0.20.3",
72
+ "yaml": "^2.8.1"
73
+ },
74
+ "devDependencies": {
75
+ "@commitlint/cli": "^20.0.0",
76
+ "@commitlint/config-conventional": "^20.0.0",
77
+ "@eslint/compat": "^1.3.2",
78
+ "@eslint/eslintrc": "^3.3.1",
79
+ "@eslint/js": "^9.35.0",
80
+ "@playwright/test": "^1.55.0",
81
+ "@sveltejs/adapter-static": "^3.0.9",
82
+ "@sveltejs/kit": "^2.37.1",
83
+ "@sveltejs/vite-plugin-svelte": "^6.1.4",
84
+ "@tailwindcss/vite": "^4.1.13",
85
+ "@types/cors": "^2.8.19",
86
+ "@types/express": "^5.0.3",
87
+ "@types/js-yaml": "^4.0.9",
88
+ "@types/jsdom": "^27.0.0",
89
+ "@types/multer": "^2.0.0",
90
+ "@types/node": "^24.4.0",
91
+ "@typescript-eslint/eslint-plugin": "^8.42.0",
92
+ "@typescript-eslint/parser": "^8.42.0",
93
+ "@vitest/browser": "^3.2.4",
94
+ "@vitest/coverage-v8": "^3.2.4",
95
+ "carbon-icons-svelte": "^13.5.0",
96
+ "carbon-preprocess-svelte": "^0.11.11",
97
+ "concurrently": "^9.2.1",
98
+ "esbuild": "^0.25.9",
99
+ "eslint": "^9.35.0",
100
+ "eslint-config-prettier": "^10.1.8",
101
+ "eslint-plugin-jsdoc": "^61.0.0",
102
+ "eslint-plugin-svelte": "^3.12.2",
103
+ "globals": "^16.3.0",
104
+ "husky": "^9.1.7",
105
+ "jsdom": "^27.0.0",
106
+ "playwright": "^1.55.0",
107
+ "prettier": "3.6.2",
108
+ "prettier-plugin-svelte": "^3.4.0",
109
+ "semantic-release": "^25.0.0",
110
+ "shellcheck": "^4.1.0",
111
+ "svelte": "^5.38.7",
112
+ "svelte-check": "^4.3.1",
113
+ "tailwind-merge": "^3.3.1",
114
+ "tailwindcss": "^4.1.13",
115
+ "tsx": "^4.20.5",
116
+ "typescript": "5.9.3",
117
+ "typescript-eslint": "^8.42.0",
118
+ "vite": "^7.1.4",
119
+ "vitest": "^3.2.4",
120
+ "vitest-browser-svelte": "^1.1.0"
121
+ },
122
+ "release": {
123
+ "branches": [
124
+ "main",
125
+ "next"
126
+ ]
127
+ }
128
128
  }
@@ -43,8 +43,8 @@
43
43
  setTimeout(() => node.focus(), 0);
44
44
  }
45
45
  return {
46
- update(newIsOpen: boolean) {
47
- if (newIsOpen) {
46
+ update() {
47
+ if (isOpen) {
48
48
  setTimeout(() => node.focus(), 0);
49
49
  }
50
50
  }
@@ -62,6 +62,8 @@
62
62
  }
63
63
 
64
64
  function handleKeydown(event: KeyboardEvent) {
65
+ if (!isOpen) return;
66
+
65
67
  if (event.key === 'Escape') {
66
68
  handleCancel();
67
69
  } else if (event.key === 'Enter') {
@@ -81,7 +83,7 @@
81
83
  {#if isOpen}
82
84
  <!-- Modal Backdrop -->
83
85
  <div
84
- class="fixed inset-0 bg-gray-700 dark:bg-gray-900 bg-opacity-50 dark:bg-opacity-75 overflow-y-auto h-full w-full z-50 flex items-center justify-center p-4"
86
+ class="fixed inset-0 bg-opacity-10 backdrop-blur-sm overflow-y-auto h-full w-full z-40 flex items-center justify-center p-4"
85
87
  role="dialog"
86
88
  aria-modal="true"
87
89
  tabindex="-1"
@@ -92,7 +94,7 @@
92
94
  <!-- svelte-ignore a11y_click_events_have_key_events -->
93
95
  <!-- svelte-ignore a11y_no_noninteractive_element_interactions -->
94
96
  <div
95
- class="bg-white dark:bg-gray-800 rounded-lg shadow-xl p-6 w-full max-w-md mx-auto"
97
+ class="bg-white dark:bg-gray-800 rounded-lg shadow-xl p-6 w-full max-w-md mx-auto relative z-50"
96
98
  role="document"
97
99
  onclick={(e) => e.stopPropagation()}
98
100
  >
@@ -125,7 +127,7 @@
125
127
  </label>
126
128
  <select
127
129
  id="column-select"
128
- use:focusSelect={isOpen}
130
+ use:focusSelect
129
131
  bind:value={selectedColumn}
130
132
  class="w-full px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-md shadow-sm bg-white dark:bg-gray-700 text-gray-900 dark:text-white focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-blue-500"
131
133
  >
@@ -173,7 +173,7 @@
173
173
  {#if $page.url.pathname !== '/setup'}
174
174
  <!-- Fixed Header -->
175
175
  <header
176
- class="bg-white dark:bg-gray-900 shadow-sm border-b border-gray-200 dark:border-gray-700 flex-shrink-0"
176
+ class="bg-white dark:bg-gray-900 shadow-sm border-b border-gray-200 dark:border-gray-700 flex-shrink-0 relative z-40"
177
177
  >
178
178
  <div class="w-full px-6 lg:px-8">
179
179
  <div class="flex justify-between items-center h-16">