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.
- package/README.md +3 -3
- package/dist/_app/immutable/assets/0.gNk4bE5Z.css +1 -0
- package/dist/_app/immutable/chunks/{DBdrJbLi.js → 6cSSDZaX.js} +1 -1
- package/dist/_app/immutable/chunks/{BaV6jU6m.js → BDFhLgJd.js} +1 -1
- package/dist/_app/immutable/chunks/{_8PUdHCK.js → BZU_Nz1O.js} +1 -1
- package/dist/_app/immutable/chunks/BnySH2DD.js +2 -0
- package/dist/_app/immutable/chunks/{sF1amBpw.js → CfxzrOg_.js} +1 -1
- package/dist/_app/immutable/chunks/{DG4ZcLIc.js → D2GS6lt_.js} +1 -1
- package/dist/_app/immutable/chunks/MY0vM1lA.js +3 -0
- package/dist/_app/immutable/chunks/{WNo48c2U.js → Vl8FMUTS.js} +1 -1
- package/dist/_app/immutable/chunks/{CynYS-Ma.js → ipJF3Ffx.js} +1 -1
- package/dist/_app/immutable/entry/{app.VdrAeFMF.js → app.XAEr8i-M.js} +2 -2
- package/dist/_app/immutable/entry/start.DCxCYu1M.js +1 -0
- package/dist/_app/immutable/nodes/0.DHn4BGVS.js +2 -0
- package/dist/_app/immutable/nodes/{1.Dt4-pegp.js → 1.BRVNZ7Zr.js} +1 -1
- package/dist/_app/immutable/nodes/{2.CgJgF2F1.js → 2.DjZVN6-v.js} +1 -1
- package/dist/_app/immutable/nodes/{3.DsDKmoyP.js → 3.BwkBTWlm.js} +1 -1
- package/dist/_app/immutable/nodes/{4.E9uJ4Np6.js → 4.CrrKXZL6.js} +1 -1
- package/dist/_app/version.json +1 -1
- package/dist/cli/commands/ui.js +49 -7
- package/dist/cli/server/index.js +49 -7
- package/dist/cli/server/server.js +49 -7
- package/dist/cli/server/serverState.js +37 -4
- package/dist/cli/server/spreadsheetRoutes.js +12 -3
- package/dist/cli/server/websocketServer.js +49 -7
- package/dist/index.html +10 -10
- package/dist/index.js +49 -7
- package/package.json +126 -126
- package/src/lib/components/dialogs/ExportColumnDialog.svelte +7 -5
- package/src/routes/+layout.svelte +1 -1
- package/dist/_app/immutable/assets/0.CJjXKESY.css +0 -1
- package/dist/_app/immutable/chunks/DmBJsPtc.js +0 -2
- package/dist/_app/immutable/chunks/DztCq-9O.js +0 -3
- package/dist/_app/immutable/entry/start.BD6MZ_XC.js +0 -1
- package/dist/_app/immutable/nodes/0.D3GkfY8V.js +0 -2
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import"../chunks/DsnmJJEf.js";import{i as We}from"../chunks/_8PUdHCK.js";import{p as Xe,am as Ve,d as a,T as k,G as pe,h as e,an as $r,ao as Ne,c as o,n as ke,r as t,b as d,e as Ke,J as P,K as I,ap as Nr,f as x,s as i,aq as c,a as we,ar as xr,aj as Ae,a3 as ze,ak as hr,al as tr,as as yr,k as de,o as ot,W as lt}from"../chunks/DmBJsPtc.js";import{l as ar,p as Ye,i as M,a as st,s as it}from"../chunks/BaV6jU6m.js";import{a as Lr,s as le,r as jr,e as ge,b as Je,c as Vr,d as _r,i as Cr,f as Sr,w as wr}from"../chunks/sF1amBpw.js";import{g as nt}from"../chunks/DztCq-9O.js";function dt(ue){return function(...U){var F=U[0];return F.stopPropagation(),ue?.apply(this,U)}}var vt=Ne("<title> </title>"),ct=Ne('<svg><!><path d="M11 18L12.41 19.41 15 16.83 15 29 17 29 17 16.83 19.59 19.41 21 18 16 13 11 18z"></path><path d="M23.5,22H23V20h.5a4.5,4.5,0,0,0,.36-9L23,11l-.1-.82a7,7,0,0,0-13.88,0L9,11,8.14,11a4.5,4.5,0,0,0,.36,9H9v2H8.5A6.5,6.5,0,0,1,7.2,9.14a9,9,0,0,1,17.6,0A6.5,6.5,0,0,1,23.5,22Z"></path></svg>');function gt(ue,U){const F=ar(U,["children","$$slots","$$events","$$legacy"]),J=ar(F,["size","title"]);Xe(U,!1);const E=k(),j=k();let Z=Ye(U,"size",8,16),b=Ye(U,"title",8,void 0);Ve(()=>(pe(F),pe(b())),()=>{a(E,F["aria-label"]||F["aria-labelledby"]||b())}),Ve(()=>(e(E),pe(F)),()=>{a(j,{"aria-hidden":e(E)?void 0:!0,role:e(E)?"img":void 0,focusable:Number(F.tabindex)===0?!0:void 0})}),$r(),We();var $=ct();Lr($,()=>({xmlns:"http://www.w3.org/2000/svg",viewBox:"0 0 32 32",fill:"currentColor",preserveAspectRatio:"xMidYMid meet",width:Z(),height:Z(),...e(j),...J}));var K=o($);{var G=H=>{var g=vt(),N=o(g,!0);t(g),P(()=>I(N,b())),d(H,g)};M(K,H=>{b()&&H(G)})}ke(2),t($),d(ue,$),Ke()}var pt=Ne("<title> </title>"),ut=Ne('<svg><!><path d="M10 6H14V10H10zM18 6H22V10H18zM10 14H14V18H10zM18 14H22V18H18zM10 22H14V26H10zM18 22H22V26H18z"></path></svg>');function kr(ue,U){const F=ar(U,["children","$$slots","$$events","$$legacy"]),J=ar(F,["size","title"]);Xe(U,!1);const E=k(),j=k();let Z=Ye(U,"size",8,16),b=Ye(U,"title",8,void 0);Ve(()=>(pe(F),pe(b())),()=>{a(E,F["aria-label"]||F["aria-labelledby"]||b())}),Ve(()=>(e(E),pe(F)),()=>{a(j,{"aria-hidden":e(E)?void 0:!0,role:e(E)?"img":void 0,focusable:Number(F.tabindex)===0?!0:void 0})}),$r(),We();var $=ut();Lr($,()=>({xmlns:"http://www.w3.org/2000/svg",viewBox:"0 0 32 32",fill:"currentColor",preserveAspectRatio:"xMidYMid meet",width:Z(),height:Z(),...e(j),...J}));var K=o($);{var G=H=>{var g=pt(),N=o(g,!0);t(g),P(()=>I(N,b())),d(H,g)};M(K,H=>{b()&&H(G)})}ke(),t($),d(ue,$),Ke()}var bt=x('<div class="p-4 text-sm text-blue-800 rounded-lg bg-blue-50 dark:bg-gray-800 dark:text-blue-400"><div class="flex items-center"><svg class="flex-shrink-0 inline w-4 h-4 mr-3" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" fill="currentColor" viewBox="0 0 20 20"><path d="M10 .5a9.5 9.5 0 1 0 9.5 9.5A9.51 9.51 0 0 0 10 .5ZM9.5 4a1.5 1.5 0 1 1 0 3 1.5 1.5 0 0 1 0-3ZM12 15H8a1 1 0 0 1 0-2h1v-3H8a1 1 0 0 1 0-2h2a1 1 0 0 1 1 1v4h1a1 1 0 0 1 0 2Z"></path></svg> <div><span class="font-medium">File loaded:</span> <div class="mt-1"><span class="font-medium">Sheets:</span> <span class="font-medium">Fields:</span> <span class="font-medium">Controls found:</span> </div></div></div></div>'),ft=x('<div class="p-4 text-sm text-red-800 rounded-lg bg-red-50 dark:bg-gray-800 dark:text-red-400"><div class="flex items-center"><svg class="flex-shrink-0 inline w-4 h-4 mr-3" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" fill="currentColor" viewBox="0 0 20 20"><path d="M10 .5a9.5 9.5 0 1 0 9.5 9.5A9.51 9.51 0 0 0 10 .5ZM9.5 4a1.5 1.5 0 1 1 0 3 1.5 1.5 0 0 1 0-3ZM12 15H8a1 1 0 0 1 0-2h1v-3H8a1 1 0 0 1 0-2h2a1 1 0 0 1 1 1v4h1a1 1 0 0 1 0 2Z"></path></svg> <span> </span></div></div>'),mt=x('<div class="p-4 text-sm text-green-800 rounded-lg bg-green-50 dark:bg-gray-800 dark:text-green-400"><div class="flex items-center"><svg class="flex-shrink-0 inline w-4 h-4 mr-3" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" fill="currentColor" viewBox="0 0 20 20"><path d="M10 .5a9.5 9.5 0 1 0 9.5 9.5A9.51 9.51 0 0 0 10 .5ZM9.5 4a1.5 1.5 0 1 1 0 3 1.5 1.5 0 0 1 0-3ZM12 15H8a1 1 0 0 1 0-2h1v-3H8a1 1 0 0 1 0-2h2a1 1 0 0 1 1 1v4h1a1 1 0 0 1 0 2Z"></path></svg> <span> </span></div></div>'),xt=x("<option> </option>"),ht=x("<option> </option>"),yt=x("<option> </option>"),_t=x('<span class="ml-auto text-xs text-blue-600 dark:text-blue-400">ID</span>'),wt=x('<div draggable="true" role="button" tabindex="0" class="flex items-center px-3 py-2 bg-gray-100 dark:bg-gray-700 text-gray-500 dark:text-gray-400 rounded text-sm cursor-move hover:bg-gray-200 dark:hover:bg-gray-600 transition-colors opacity-75"><svg class="w-3 h-3 mr-2 flex-shrink-0" fill="currentColor" viewBox="0 0 20 20"><path d="M10 6a2 2 0 110-4 2 2 0 010 4zM10 12a2 2 0 110-4 2 2 0 010 4zM10 18a2 2 0 110-4 2 2 0 010 4z"></path></svg> <span class="truncate line-through"> </span> <!></div>'),kt=x('<p class="text-xs text-gray-400 dark:text-gray-500 text-center py-4">No excluded fields</p>'),Ct=x('<div draggable="true" role="button" tabindex="0"><!> <span class="truncate"> </span></div>'),St=x('<p class="text-xs text-gray-400 dark:text-gray-500 text-center py-4">Drop fields here</p>'),$t=x('<div draggable="true" role="button" tabindex="0"><!> <span class="truncate"> </span></div>'),Dt=x('<p class="text-xs text-gray-400 dark:text-gray-500 text-center py-4">Drop fields here</p>'),Mt=x('<div draggable="true" role="button" tabindex="0"><!> <span class="truncate"> </span></div>'),zt=x('<p class="text-xs text-gray-400 dark:text-gray-500 text-center py-4">Drop fields here</p>'),At=x('<div draggable="false" role="button" tabindex="0" class="flex items-center px-3 py-2 bg-orange-100 dark:bg-orange-900/30 text-orange-800 dark:text-orange-300 rounded text-sm hover:bg-orange-200 dark:hover:bg-orange-800/30 transition-colors"><span class="truncate"> </span> <button class="ml-auto text-gray-400 hover:text-red-500 dark:text-gray-500 dark:hover:text-red-400" title="Remove from mappings">×</button></div>'),Ft=x('<div role="region" aria-label="Justification field drop zone"><p class="text-xs text-gray-400 dark:text-gray-500 text-center py-4">Drop fields here</p></div>'),Ot=x('<th class="px-4 py-2"> </th>'),Pt=x('<td class="px-4 py-2"> </td>'),Ht=x('<tr class="bg-white border-b dark:bg-gray-800 dark:border-gray-700"></tr>'),Et=x('<div class="bg-gradient-to-br from-gray-50 to-gray-100 dark:from-gray-800 dark:to-gray-900 rounded-lg p-4 border border-gray-200 dark:border-gray-700"><h3 class="text-lg font-semibold text-gray-900 dark:text-white mb-4">Sample Data Preview</h3> <div class="overflow-x-auto"><table class="w-full text-sm text-left text-gray-500 dark:text-gray-400"><thead class="text-xs text-gray-700 uppercase bg-gray-100 dark:bg-gray-600 dark:text-gray-400"><tr></tr></thead><tbody></tbody></table></div></div>'),It=x('<span class="flex items-center"><svg class="animate-spin -ml-1 mr-3 h-5 w-5 text-white" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24"><circle class="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" stroke-width="4"></circle><path class="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"></path></svg> Importing...</span>'),Tt=x(`<div class="bg-gradient-to-br from-gray-50 to-gray-100 dark:from-gray-800 dark:to-gray-900 rounded-lg p-4 border border-gray-200 dark:border-gray-700"><h3 class="text-lg font-semibold text-gray-900 dark:text-white mb-4">Import Options</h3> <div class="grid grid-cols-1 md:grid-cols-2 gap-4 mb-4"><div><label for="controlSetName" class="block mb-2 text-sm font-medium text-gray-900 dark:text-white">Control Set Name <span class="text-red-500">*</span></label> <input type="text" id="controlSetName" placeholder="e.g., NIST 800-53 Rev 4" class="bg-white border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 dark:bg-gray-600 dark:border-gray-500 dark:text-white" required/> <p class="mt-1 text-xs text-gray-500 dark:text-gray-400">This will be used as the display name and folder name</p></div> <div><label for="controlSetDescription" class="block mb-2 text-sm font-medium text-gray-900 dark:text-white">Description</label> <input type="text" id="controlSetDescription" placeholder="Optional description" class="bg-white border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 dark:bg-gray-600 dark:border-gray-500 dark:text-white"/> <p class="mt-1 text-xs text-gray-500 dark:text-gray-400">Brief description of this control set</p></div></div> <div class="grid grid-cols-1 md:grid-cols-2 gap-4"><div><label for="sheet" class="block mb-2 text-sm font-medium text-gray-900 dark:text-white">Sheet</label> <select id="sheet" class="bg-white border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 dark:bg-gray-600 dark:border-gray-500 dark:text-white"></select> <p class="mt-1 text-xs text-gray-500 dark:text-gray-400">Select which worksheet contains your control data</p></div> <div><label for="headerRow" class="block mb-2 text-sm font-medium text-gray-900 dark:text-white">Select Header Row</label> <select id="headerRow" class="bg-white border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 dark:bg-gray-600 dark:border-gray-500 dark:text-white"></select> <p class="mt-1 text-xs text-gray-500 dark:text-gray-400">Select the row containing column headers</p></div> <div><label for="controlIdField" class="block mb-2 text-sm font-medium text-gray-900 dark:text-white">Control ID Field <span class="text-red-500">*</span></label> <select id="controlIdField" required><option disabled>Select Control ID field</option><!></select> <p class="mt-1 text-xs text-gray-500 dark:text-gray-400">Column containing unique control identifiers (e.g., AC-1, SC-7)</p></div></div></div> <div class="bg-gradient-to-br from-gray-50 to-gray-100 dark:from-gray-800 dark:to-gray-900 rounded-lg p-4 border border-gray-200 dark:border-gray-700"><h3 class="text-lg font-semibold text-gray-900 dark:text-white mb-2">Organize Fields</h3> <p class="text-sm text-gray-600 dark:text-gray-400 mb-4">Drag fields to organize them. <strong>Overview fields</strong> will appear as table columns in
|
|
1
|
+
import"../chunks/DsnmJJEf.js";import{i as We}from"../chunks/BZU_Nz1O.js";import{p as Xe,am as Ve,d as a,T as k,G as pe,h as e,an as $r,ao as Ne,c as o,n as ke,r as t,b as d,e as Ke,J as P,K as I,ap as Nr,f as x,s as i,aq as c,a as we,ar as xr,aj as Ae,a3 as ze,ak as hr,al as tr,as as yr,k as de,o as ot,W as lt}from"../chunks/BnySH2DD.js";import{l as ar,p as Ye,i as M,a as st,s as it}from"../chunks/BDFhLgJd.js";import{a as Lr,s as le,r as jr,e as ge,b as Je,c as Vr,d as _r,i as Cr,f as Sr,w as wr}from"../chunks/CfxzrOg_.js";import{g as nt}from"../chunks/MY0vM1lA.js";function dt(ue){return function(...U){var F=U[0];return F.stopPropagation(),ue?.apply(this,U)}}var vt=Ne("<title> </title>"),ct=Ne('<svg><!><path d="M11 18L12.41 19.41 15 16.83 15 29 17 29 17 16.83 19.59 19.41 21 18 16 13 11 18z"></path><path d="M23.5,22H23V20h.5a4.5,4.5,0,0,0,.36-9L23,11l-.1-.82a7,7,0,0,0-13.88,0L9,11,8.14,11a4.5,4.5,0,0,0,.36,9H9v2H8.5A6.5,6.5,0,0,1,7.2,9.14a9,9,0,0,1,17.6,0A6.5,6.5,0,0,1,23.5,22Z"></path></svg>');function gt(ue,U){const F=ar(U,["children","$$slots","$$events","$$legacy"]),J=ar(F,["size","title"]);Xe(U,!1);const E=k(),j=k();let Z=Ye(U,"size",8,16),b=Ye(U,"title",8,void 0);Ve(()=>(pe(F),pe(b())),()=>{a(E,F["aria-label"]||F["aria-labelledby"]||b())}),Ve(()=>(e(E),pe(F)),()=>{a(j,{"aria-hidden":e(E)?void 0:!0,role:e(E)?"img":void 0,focusable:Number(F.tabindex)===0?!0:void 0})}),$r(),We();var $=ct();Lr($,()=>({xmlns:"http://www.w3.org/2000/svg",viewBox:"0 0 32 32",fill:"currentColor",preserveAspectRatio:"xMidYMid meet",width:Z(),height:Z(),...e(j),...J}));var K=o($);{var G=H=>{var g=vt(),N=o(g,!0);t(g),P(()=>I(N,b())),d(H,g)};M(K,H=>{b()&&H(G)})}ke(2),t($),d(ue,$),Ke()}var pt=Ne("<title> </title>"),ut=Ne('<svg><!><path d="M10 6H14V10H10zM18 6H22V10H18zM10 14H14V18H10zM18 14H22V18H18zM10 22H14V26H10zM18 22H22V26H18z"></path></svg>');function kr(ue,U){const F=ar(U,["children","$$slots","$$events","$$legacy"]),J=ar(F,["size","title"]);Xe(U,!1);const E=k(),j=k();let Z=Ye(U,"size",8,16),b=Ye(U,"title",8,void 0);Ve(()=>(pe(F),pe(b())),()=>{a(E,F["aria-label"]||F["aria-labelledby"]||b())}),Ve(()=>(e(E),pe(F)),()=>{a(j,{"aria-hidden":e(E)?void 0:!0,role:e(E)?"img":void 0,focusable:Number(F.tabindex)===0?!0:void 0})}),$r(),We();var $=ut();Lr($,()=>({xmlns:"http://www.w3.org/2000/svg",viewBox:"0 0 32 32",fill:"currentColor",preserveAspectRatio:"xMidYMid meet",width:Z(),height:Z(),...e(j),...J}));var K=o($);{var G=H=>{var g=pt(),N=o(g,!0);t(g),P(()=>I(N,b())),d(H,g)};M(K,H=>{b()&&H(G)})}ke(),t($),d(ue,$),Ke()}var bt=x('<div class="p-4 text-sm text-blue-800 rounded-lg bg-blue-50 dark:bg-gray-800 dark:text-blue-400"><div class="flex items-center"><svg class="flex-shrink-0 inline w-4 h-4 mr-3" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" fill="currentColor" viewBox="0 0 20 20"><path d="M10 .5a9.5 9.5 0 1 0 9.5 9.5A9.51 9.51 0 0 0 10 .5ZM9.5 4a1.5 1.5 0 1 1 0 3 1.5 1.5 0 0 1 0-3ZM12 15H8a1 1 0 0 1 0-2h1v-3H8a1 1 0 0 1 0-2h2a1 1 0 0 1 1 1v4h1a1 1 0 0 1 0 2Z"></path></svg> <div><span class="font-medium">File loaded:</span> <div class="mt-1"><span class="font-medium">Sheets:</span> <span class="font-medium">Fields:</span> <span class="font-medium">Controls found:</span> </div></div></div></div>'),ft=x('<div class="p-4 text-sm text-red-800 rounded-lg bg-red-50 dark:bg-gray-800 dark:text-red-400"><div class="flex items-center"><svg class="flex-shrink-0 inline w-4 h-4 mr-3" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" fill="currentColor" viewBox="0 0 20 20"><path d="M10 .5a9.5 9.5 0 1 0 9.5 9.5A9.51 9.51 0 0 0 10 .5ZM9.5 4a1.5 1.5 0 1 1 0 3 1.5 1.5 0 0 1 0-3ZM12 15H8a1 1 0 0 1 0-2h1v-3H8a1 1 0 0 1 0-2h2a1 1 0 0 1 1 1v4h1a1 1 0 0 1 0 2Z"></path></svg> <span> </span></div></div>'),mt=x('<div class="p-4 text-sm text-green-800 rounded-lg bg-green-50 dark:bg-gray-800 dark:text-green-400"><div class="flex items-center"><svg class="flex-shrink-0 inline w-4 h-4 mr-3" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" fill="currentColor" viewBox="0 0 20 20"><path d="M10 .5a9.5 9.5 0 1 0 9.5 9.5A9.51 9.51 0 0 0 10 .5ZM9.5 4a1.5 1.5 0 1 1 0 3 1.5 1.5 0 0 1 0-3ZM12 15H8a1 1 0 0 1 0-2h1v-3H8a1 1 0 0 1 0-2h2a1 1 0 0 1 1 1v4h1a1 1 0 0 1 0 2Z"></path></svg> <span> </span></div></div>'),xt=x("<option> </option>"),ht=x("<option> </option>"),yt=x("<option> </option>"),_t=x('<span class="ml-auto text-xs text-blue-600 dark:text-blue-400">ID</span>'),wt=x('<div draggable="true" role="button" tabindex="0" class="flex items-center px-3 py-2 bg-gray-100 dark:bg-gray-700 text-gray-500 dark:text-gray-400 rounded text-sm cursor-move hover:bg-gray-200 dark:hover:bg-gray-600 transition-colors opacity-75"><svg class="w-3 h-3 mr-2 flex-shrink-0" fill="currentColor" viewBox="0 0 20 20"><path d="M10 6a2 2 0 110-4 2 2 0 010 4zM10 12a2 2 0 110-4 2 2 0 010 4zM10 18a2 2 0 110-4 2 2 0 010 4z"></path></svg> <span class="truncate line-through"> </span> <!></div>'),kt=x('<p class="text-xs text-gray-400 dark:text-gray-500 text-center py-4">No excluded fields</p>'),Ct=x('<div draggable="true" role="button" tabindex="0"><!> <span class="truncate"> </span></div>'),St=x('<p class="text-xs text-gray-400 dark:text-gray-500 text-center py-4">Drop fields here</p>'),$t=x('<div draggable="true" role="button" tabindex="0"><!> <span class="truncate"> </span></div>'),Dt=x('<p class="text-xs text-gray-400 dark:text-gray-500 text-center py-4">Drop fields here</p>'),Mt=x('<div draggable="true" role="button" tabindex="0"><!> <span class="truncate"> </span></div>'),zt=x('<p class="text-xs text-gray-400 dark:text-gray-500 text-center py-4">Drop fields here</p>'),At=x('<div draggable="false" role="button" tabindex="0" class="flex items-center px-3 py-2 bg-orange-100 dark:bg-orange-900/30 text-orange-800 dark:text-orange-300 rounded text-sm hover:bg-orange-200 dark:hover:bg-orange-800/30 transition-colors"><span class="truncate"> </span> <button class="ml-auto text-gray-400 hover:text-red-500 dark:text-gray-500 dark:hover:text-red-400" title="Remove from mappings">×</button></div>'),Ft=x('<div role="region" aria-label="Justification field drop zone"><p class="text-xs text-gray-400 dark:text-gray-500 text-center py-4">Drop fields here</p></div>'),Ot=x('<th class="px-4 py-2"> </th>'),Pt=x('<td class="px-4 py-2"> </td>'),Ht=x('<tr class="bg-white border-b dark:bg-gray-800 dark:border-gray-700"></tr>'),Et=x('<div class="bg-gradient-to-br from-gray-50 to-gray-100 dark:from-gray-800 dark:to-gray-900 rounded-lg p-4 border border-gray-200 dark:border-gray-700"><h3 class="text-lg font-semibold text-gray-900 dark:text-white mb-4">Sample Data Preview</h3> <div class="overflow-x-auto"><table class="w-full text-sm text-left text-gray-500 dark:text-gray-400"><thead class="text-xs text-gray-700 uppercase bg-gray-100 dark:bg-gray-600 dark:text-gray-400"><tr></tr></thead><tbody></tbody></table></div></div>'),It=x('<span class="flex items-center"><svg class="animate-spin -ml-1 mr-3 h-5 w-5 text-white" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24"><circle class="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" stroke-width="4"></circle><path class="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"></path></svg> Importing...</span>'),Tt=x(`<div class="bg-gradient-to-br from-gray-50 to-gray-100 dark:from-gray-800 dark:to-gray-900 rounded-lg p-4 border border-gray-200 dark:border-gray-700"><h3 class="text-lg font-semibold text-gray-900 dark:text-white mb-4">Import Options</h3> <div class="grid grid-cols-1 md:grid-cols-2 gap-4 mb-4"><div><label for="controlSetName" class="block mb-2 text-sm font-medium text-gray-900 dark:text-white">Control Set Name <span class="text-red-500">*</span></label> <input type="text" id="controlSetName" placeholder="e.g., NIST 800-53 Rev 4" class="bg-white border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 dark:bg-gray-600 dark:border-gray-500 dark:text-white" required/> <p class="mt-1 text-xs text-gray-500 dark:text-gray-400">This will be used as the display name and folder name</p></div> <div><label for="controlSetDescription" class="block mb-2 text-sm font-medium text-gray-900 dark:text-white">Description</label> <input type="text" id="controlSetDescription" placeholder="Optional description" class="bg-white border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 dark:bg-gray-600 dark:border-gray-500 dark:text-white"/> <p class="mt-1 text-xs text-gray-500 dark:text-gray-400">Brief description of this control set</p></div></div> <div class="grid grid-cols-1 md:grid-cols-2 gap-4"><div><label for="sheet" class="block mb-2 text-sm font-medium text-gray-900 dark:text-white">Sheet</label> <select id="sheet" class="bg-white border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 dark:bg-gray-600 dark:border-gray-500 dark:text-white"></select> <p class="mt-1 text-xs text-gray-500 dark:text-gray-400">Select which worksheet contains your control data</p></div> <div><label for="headerRow" class="block mb-2 text-sm font-medium text-gray-900 dark:text-white">Select Header Row</label> <select id="headerRow" class="bg-white border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 dark:bg-gray-600 dark:border-gray-500 dark:text-white"></select> <p class="mt-1 text-xs text-gray-500 dark:text-gray-400">Select the row containing column headers</p></div> <div><label for="controlIdField" class="block mb-2 text-sm font-medium text-gray-900 dark:text-white">Control ID Field <span class="text-red-500">*</span></label> <select id="controlIdField" required><option disabled>Select Control ID field</option><!></select> <p class="mt-1 text-xs text-gray-500 dark:text-gray-400">Column containing unique control identifiers (e.g., AC-1, SC-7)</p></div></div></div> <div class="bg-gradient-to-br from-gray-50 to-gray-100 dark:from-gray-800 dark:to-gray-900 rounded-lg p-4 border border-gray-200 dark:border-gray-700"><h3 class="text-lg font-semibold text-gray-900 dark:text-white mb-2">Organize Fields</h3> <p class="text-sm text-gray-600 dark:text-gray-400 mb-4">Drag fields to organize them. <strong>Overview fields</strong> will appear as table columns in
|
|
2
2
|
the controls list.</p> <div class="grid grid-cols-1 lg:grid-cols-5 gap-4"><div class="border border-gray-300 dark:border-gray-600 rounded-lg bg-white dark:bg-gray-800"><div class="p-3 border-b border-gray-200 dark:border-gray-700 bg-gray-50 dark:bg-gray-700 rounded-t-lg"><h4 class="text-sm font-semibold text-gray-700 dark:text-gray-300">Excluded Fields</h4> <p class="text-xs text-gray-500 dark:text-gray-400 mt-1">Not imported</p></div> <div role="region" aria-label="Excluded fields drop zone"><!> <!></div></div> <div class="border border-blue-300 dark:border-blue-700 rounded-lg bg-white dark:bg-gray-800"><div class="p-3 border-b border-blue-200 dark:border-blue-800 bg-blue-50 dark:bg-blue-900/20 rounded-t-lg"><h4 class="text-sm font-semibold text-blue-700 dark:text-blue-300">Overview Tab</h4> <p class="text-xs text-blue-600 dark:text-blue-400 mt-1">Shows in details & table columns</p></div> <div role="region" aria-label="Overview tab drop zone"><!> <!></div></div> <div class="border border-green-300 dark:border-green-700 rounded-lg bg-white dark:bg-gray-800"><div class="p-3 border-b border-green-200 dark:border-green-800 bg-green-50 dark:bg-green-900/20 rounded-t-lg"><h4 class="text-sm font-semibold text-green-700 dark:text-green-300">Implementation Tab</h4> <p class="text-xs text-green-600 dark:text-green-400 mt-1">Status & compliance</p></div> <div role="region" aria-label="Implementation tab drop zone"><!> <!></div></div> <div class="border border-purple-300 dark:border-purple-700 rounded-lg bg-white dark:bg-gray-800"><div class="p-3 border-b border-purple-200 dark:border-purple-800 bg-purple-50 dark:bg-purple-900/20 rounded-t-lg"><h4 class="text-sm font-semibold text-purple-700 dark:text-purple-300">Custom Tab</h4> <p class="text-xs text-purple-600 dark:text-purple-400 mt-1">Additional fields</p></div> <div role="region" aria-label="Custom fields drop zone"><!> <!></div></div> <div class="border border-orange-300 dark:border-orange-700 rounded-lg bg-white dark:bg-gray-800"><div class="p-3 border-b border-orange-200 dark:border-orange-800 bg-orange-50 dark:bg-orange-900/20 rounded-t-lg"><h4 class="text-sm font-semibold text-orange-700 dark:text-orange-300">Mappings Tab</h4> <p class="text-xs text-orange-600 dark:text-orange-400 mt-1">Pre-populate justification for a control mapping</p></div> <div role="region" aria-label="Justifications tab drop zone"><div class="space-y-2"><!></div></div></div></div></div> <!> <div class="flex justify-center"><button class="px-5 py-2.5 text-white bg-blue-600 hover:bg-blue-700 focus:ring-4 focus:ring-blue-300 font-medium rounded-lg text-sm disabled:opacity-50 disabled:cursor-not-allowed"><!></button></div>`,1),jt=x('<div class="space-y-6"><div role="button" tabindex="0" class="relative"><label><div class="flex flex-col items-center justify-center pt-5 pb-6"><!> <p class="mb-2 text-sm text-gray-500 dark:text-gray-400"><span class="font-semibold">Click to upload</span> or drag and drop</p> <p class="text-xs text-gray-500 dark:text-gray-400">XLSX, XLS or CSV files</p></div> <input type="file" class="hidden" accept=".xlsx,.xls,.csv"/></label></div> <!> <!> <!> <!></div>');function Vt(ue,U){Xe(U,!1);const F=Nr();let J=k(null),E=k(""),j=k(""),Z=k([]),b=k([]),$=k([]),K=k(0),G=k([]),H=k([]),g=k(new Map),N=k(1),L=k(""),xe=k(""),Y=k(""),Q=k(!1),ee=k(""),be=k(""),Ce=k(!1),te=k(!1),C=k(null),B=k(null),re=k(null);function Se(){a(b,[]),a($,[]),a(K,0),e(g).clear(),a(g,new Map),a(H,[]),a(L,""),a(ee,""),a(be,""),a(C,null),a(B,null),a(re,null)}function p(l){l.preventDefault(),a(te,!0)}function u(){a(te,!1)}function D(l){l.preventDefault(),a(te,!1);const n=l.dataTransfer?.files;n&&n.length>0&&T(n[0])}function V(l){const n=l.target;n.files&&n.files.length>0&&T(n.files[0])}async function T(l){Se(),a(E,l.name),a(J,l),a(ee,""),a(Q,!0),a(xe,e(E).replace(/\.[^.]+$/,"").replace(/[-_]/g," ")),a(Y,`Imported from ${e(E)}`);try{const n=new FormData;n.append("file",l);const y=await fetch("/api/parse-excel",{method:"POST",body:n});if(!y.ok){const z=await y.json();throw new Error(z.error||"Failed to parse file")}const h=await y.json();a(Z,h.sheets||[]),a(j,h.selectedSheet||e(Z)[0]),a(G,h.rowPreviews||[]),e(G).length>0&&e(N)===1&&a(N,e(G)[0].row),await q(),a(Ce,!0)}catch(n){a(ee,"Error reading file: "+n.message)}finally{a(Q,!1)}}async function q(){if(!(!e(J)||!e(j))){a(Q,!0),e(g).clear(),a(g,new Map),a(L,""),a(H,[]),a(C,null),a(B,null),a(re,null);try{const l=new FormData;l.append("file",e(J)),l.append("sheetName",e(j));const n=await fetch("/api/parse-excel-sheet-previews",{method:"POST",body:l});if(n.ok){const w=await n.json();a(G,w.rowPreviews||[]),e(G).length>0&&!e(G).some(A=>A.row===e(N))&&a(N,e(G)[0].row)}else{const w=await n.json();throw new Error(w.error||"Failed to load sheet previews")}const y=new FormData;y.append("file",e(J)),y.append("sheetName",e(j)),y.append("headerRow",e(N).toString());const h=await fetch("/api/parse-excel-sheet",{method:"POST",body:y});if(!h.ok){const w=await h.json();throw new Error(w.error||"Failed to parse sheet")}const z=await h.json();if(a(b,z.fields||[]),a($,z.sampleData||[]),a(K,z.controlCount||0),e(b).forEach((w,A)=>{const _=w.toLowerCase();let ce="custom",fe="text";_.includes("implementation")||_.includes("status")||_.includes("narrative")||_.includes("guidance")?ce="implementation":(_.includes("id")||_.includes("title")||_.includes("family")||_.includes("cci")||_.includes("control")||_.includes("acronym"))&&(ce="overview"),_.includes("description")||_.includes("narrative")||_.includes("guidance")||_.includes("statement")?fe="textarea":_.includes("status")||_.includes("type")||_.includes("designation")?fe="select":_.includes("date")?fe="date":(_.includes("count")||_.includes("number"))&&(fe="number"),e(g).set(w,{originalName:w,tab:ce,displayOrder:A,fieldType:fe,required:_.includes("id")||_.includes("title")})}),a(g,e(g)),e(L)&&!e(b).includes(e(L))&&a(L,""),!e(L)&&e(b).includes("AP Acronym")){const w=!e($).length||e($).every(ne=>!ne["AP Acronym"]||String(ne["AP Acronym"]).length<25),A=e($).map(ne=>ne["AP Acronym"]).filter(ne=>ne!=null&&ne!==""&&String(ne).trim()!==""),_=new Set(A),ce=!A.length||_.size===A.length,fe=A.length>0;w&&ce&&fe&&a(L,"AP Acronym")}}catch(l){a(ee,"Error loading sheet data: "+l.message)}finally{a(Q,!1)}}}function ve(l){if(!l)return l;let n=l.trim().replace(/\r?\n/g," ").replace(/\s+/g," ").trim();return Le(n)}function Le(l){return l.replace(/\W+/g," ").split(/ |\s/).map(n=>n.toLowerCase()).join("-")}function he(l,n){a(C,n),l.dataTransfer&&(l.dataTransfer.effectAllowed="move",l.dataTransfer.setData("text/plain",n))}function ae(){a(C,null),a(B,null),a(re,null)}function S(l,n){l.preventDefault(),a(B,n),l.dataTransfer&&(l.dataTransfer.dropEffect="move")}function se(){a(B,null)}function ie(l,n,y){if(l.preventDefault(),e(C)&&e(g).has(e(C))){const h=e(g).get(e(C));if(n==="mappings")e(H).includes(e(C))||a(H,[...e(H),e(C)]);else{if(h.tab=n,y!==void 0&&n!==null){const w=Array.from(e(g).entries()).filter(([A,_])=>_.tab===n).sort((A,_)=>A[1].displayOrder-_[1].displayOrder).filter(([A])=>A!==e(C));w.splice(y,0,[e(C),h]),w.forEach(([A,_],ce)=>{_.displayOrder=ce,e(g).set(A,_)})}else if(n!==null){const z=Math.max(0,...Array.from(e(g).values()).filter(w=>w.tab===n).map(w=>w.displayOrder));h.displayOrder=z+1}e(g).set(e(C),h),a(g,e(g))}}a(C,null),a(B,null)}function Fe(l,n){l.preventDefault(),l.stopPropagation(),a(re,n)}function $e(){a(re,null)}function De(l,n,y){if(l.preventDefault(),l.stopPropagation(),e(C)&&e(C)!==n){const z=Array.from(e(g).entries()).filter(([w,A])=>A.tab===y).sort((w,A)=>w[1].displayOrder-A[1].displayOrder).findIndex(([w])=>w===n);z!==-1&&ie(l,y,z)}a(re,null)}async function Be(){if(!(!e(J)||!e(E))){if(!e(L)){a(ee,"Please select a Control ID field before importing"),a(be,"");return}if(!e(xe)||e(xe).trim()===""){a(ee,"Please enter a Control Set Name before importing"),a(be,"");return}a(Q,!0),a(ee,""),a(be,"");try{const l=new FormData;l.append("file",e(J),e(E)),l.append("controlIdField",e(L)),l.append("startRow",e(N).toString()),l.append("sheetName",e(j)),l.append("namingConvention","kebab-case"),l.append("skipEmpty","true"),l.append("skipEmptyRows","true"),l.append("controlSetName",e(xe)||e(E).replace(/\.[^.]+$/,"")),l.append("controlSetDescription",e(Y)||`Imported from ${e(E)}`);const n=Array.from(e(g).entries()).filter(([h,z])=>z.tab!==null).map(([h,z])=>({fieldName:ve(h),...z}));l.append("fieldSchema",JSON.stringify(n)),l.append("justificationFields",JSON.stringify(e(H).map(h=>ve(h))));let y;try{if(y=await fetch("/api/import-spreadsheet",{method:"POST",body:l}),!y.ok){const z=await y.json();throw new Error(z.error||"Import failed")}const h=await y.json();a(be,`Successfully imported ${h.controlCount} controls into ${h.families.length} families`),F("created",{path:h.outputDir})}catch(h){console.error("Error importing spreadsheet:",h)}}catch(l){a(ee,"Error importing spreadsheet: "+l.message)}finally{a(Q,!1)}}}We();var Re=jt(),ye=o(Re),Ze=o(ye),Oe=o(Ze),or=o(Oe);gt(or,{class:"w-8 h-8 mb-4 text-gray-500 dark:text-gray-400"}),ke(4),t(Oe);var Ge=i(Oe,2);t(Ze),t(ye);var Pe=i(ye,2);{var lr=l=>{var n=bt(),y=o(n),h=i(o(y),2),z=i(o(h)),w=i(z),A=i(o(w)),_=i(A,2),ce=i(_,2);t(w),t(h),t(y),t(n),P(()=>{I(z,` ${e(E)??""} `),I(A,` ${e(Z).length??""} | `),I(_,` ${e(b).length??""} | `),I(ce,` ${e(K)??""}`)}),d(l,n)};M(Pe,l=>{e(E)&&l(lr)})}var Qe=i(Pe,2);{var sr=l=>{var n=ft(),y=o(n),h=i(o(y),2),z=o(h,!0);t(h),t(y),t(n),P(()=>I(z,e(ee))),d(l,n)};M(Qe,l=>{e(ee)&&l(sr)})}var qe=i(Qe,2);{var ir=l=>{var n=mt(),y=o(n),h=i(o(y),2),z=o(h,!0);t(h),t(y),t(n),P(()=>I(z,e(be))),d(l,n)};M(qe,l=>{e(be)&&l(ir)})}var nr=i(qe,2);{var dr=l=>{var n=Tt(),y=we(n),h=i(o(y),2),z=o(h),w=i(o(z),2);jr(w),ke(2),t(z);var A=i(z,2),_=i(o(A),2);jr(_),ke(2),t(A),t(h);var ce=i(h,2),fe=o(ce),ne=i(o(fe),2);P(()=>{e(j),xr(()=>{e(Z)})}),ge(ne,5,()=>e(Z),r=>r,(r,s)=>{var f=xt(),m=o(f,!0);t(f);var v={};P(()=>{I(m,e(s)),v!==(v=e(s))&&(f.value=(f.__value=e(s))??"")}),d(r,f)}),t(ne),ke(2),t(fe);var vr=i(fe,2),er=i(o(vr),2);P(()=>{e(N),xr(()=>{e(G)})}),ge(er,5,()=>e(G),r=>r.row,(r,s)=>{var f=ht(),m=o(f);t(f);var v={};P(()=>{I(m,`Row ${e(s).row??""}: ${e(s).preview??""}`),v!==(v=e(s).row)&&(f.value=(f.__value=e(s).row)??"")}),d(r,f)}),t(er),ke(2),t(vr);var Dr=i(vr,2),rr=i(o(Dr),2);P(()=>{e(L),xr(()=>{e(b),e($)})});var cr=o(rr);cr.value=cr.__value="";var Br=i(cr);ge(Br,1,()=>e(b),r=>r,(r,s)=>{const f=ze(()=>e($).length>0&&e($)[0][e(s)]?String(e($)[0][e(s)]).slice(0,30):""),m=ze(()=>!e($).length||e($).every(oe=>!oe[e(s)]||String(oe[e(s)]).length<25)),v=ze(()=>e($).map(oe=>oe[e(s)]).filter(oe=>oe!=null&&oe!==""&&String(oe).trim()!=="")),W=ze(()=>new Set(e(v))),X=ze(()=>!e(v).length||e(W).size===e(v).length),R=ze(()=>e(v).length>0);var O=Ae(),me=we(O);{var Me=oe=>{var _e=yt(),mr=o(_e);t(_e);var Tr={};P(()=>{I(mr,`${e(s)??""}${e(f)?` (e.g., ${e(f)})`:""}`),Tr!==(Tr=e(s))&&(_e.value=(_e.__value=e(s))??"")}),d(oe,_e)};M(me,oe=>{e(m)&&e(X)&&e(R)&&oe(Me)})}d(r,O)}),t(rr),ke(2),t(Dr),t(ce),t(y);var gr=i(y,2),Mr=i(o(gr),4),pr=o(Mr),He=i(o(pr),2),zr=o(He);ge(zr,1,()=>e(b).filter(r=>!e(g).get(r)||e(g).get(r)?.tab===null),r=>r,(r,s)=>{var f=wt(),m=i(o(f),2),v=o(m,!0);t(m);var W=i(m,2);{var X=R=>{var O=_t();d(R,O)};M(W,R=>{e(s)===e(L)&&R(X)})}t(f),P(()=>{Je(f,"aria-label",`Drag ${e(s)??""} field`),I(v,e(s))}),c("dragstart",f,R=>he(R,e(s))),c("dragend",f,ae),d(r,f)});var Rr=i(zr,2);{var Zr=r=>{var s=kt();d(r,s)};M(Rr,r=>{e(b).filter(s=>!e(g).get(s)||e(g).get(s)?.tab===null).length===0&&r(Zr)})}t(He),t(pr);var ur=i(pr,2),Ee=i(o(ur),2),Ar=o(Ee);ge(Ar,3,()=>Array.from(e(g).entries()).filter(([r,s])=>s.tab==="overview").sort((r,s)=>r[1].displayOrder-s[1].displayOrder),([r,s])=>r,(r,s)=>{var f=hr(()=>yr(e(s),2));let m=()=>e(f)[0];var v=Ct(),W=o(v);kr(W,{class:"w-3 h-3 mr-2 flex-shrink-0"});var X=i(W,2),R=o(X,!0);t(X),t(v),P(()=>{Je(v,"aria-label",`${m()??""} field in Overview tab`),le(v,1,`flex items-center px-3 py-2 bg-blue-100 dark:bg-blue-900/30 text-blue-800 dark:text-blue-300 rounded text-sm cursor-move hover:bg-blue-200 dark:hover:bg-blue-800/30 transition-colors
|
|
3
3
|
${e(re)===m()&&e(C)!==m()?"border-t-2 border-blue-500":""}`),I(R,m())}),c("dragstart",v,O=>he(O,m())),c("dragend",v,ae),c("dragover",v,O=>Fe(O,m())),c("dragleave",v,$e),c("drop",v,O=>De(O,m(),"overview")),d(r,v)});var qr=i(Ar,2);{var Ur=r=>{var s=St();d(r,s)};M(qr,r=>{Array.from(e(g).entries()).filter(([s,f])=>f.tab==="overview").length===0&&r(Ur)})}t(Ee),t(ur);var br=i(ur,2),Ie=i(o(br),2),Fr=o(Ie);ge(Fr,3,()=>Array.from(e(g).entries()).filter(([r,s])=>s.tab==="implementation").sort((r,s)=>r[1].displayOrder-s[1].displayOrder),([r,s])=>r,(r,s)=>{var f=hr(()=>yr(e(s),2));let m=()=>e(f)[0];var v=$t(),W=o(v);kr(W,{class:"w-3 h-3 mr-2 flex-shrink-0"});var X=i(W,2),R=o(X,!0);t(X),t(v),P(()=>{Je(v,"aria-label",`${m()??""} field in Implementation tab`),le(v,1,`flex items-center px-3 py-2 bg-green-100 dark:bg-green-900/30 text-green-800 dark:text-green-300 rounded text-sm cursor-move hover:bg-green-200 dark:hover:bg-green-800/30 transition-colors
|
|
4
4
|
${e(re)===m()&&e(C)!==m()?"border-t-2 border-green-500":""}`),I(R,m())}),c("dragstart",v,O=>he(O,m())),c("dragend",v,ae),c("dragover",v,O=>Fe(O,m())),c("dragleave",v,$e),c("drop",v,O=>De(O,m(),"implementation")),d(r,v)});var Jr=i(Fr,2);{var Yr=r=>{var s=Dt();d(r,s)};M(Jr,r=>{Array.from(e(g).entries()).filter(([s,f])=>f.tab==="implementation").length===0&&r(Yr)})}t(Ie),t(br);var fr=i(br,2),Te=i(o(fr),2),Or=o(Te);ge(Or,3,()=>Array.from(e(g).entries()).filter(([r,s])=>s.tab==="custom").sort((r,s)=>r[1].displayOrder-s[1].displayOrder),([r,s])=>r,(r,s)=>{var f=hr(()=>yr(e(s),2));let m=()=>e(f)[0];var v=Mt(),W=o(v);kr(W,{class:"w-3 h-3 mr-2 flex-shrink-0"});var X=i(W,2),R=o(X,!0);t(X),t(v),P(()=>{Je(v,"aria-label",`${m()??""} field in Custom tab`),le(v,1,`flex items-center px-3 py-2 bg-purple-100 dark:bg-purple-900/30 text-purple-800 dark:text-purple-300 rounded text-sm cursor-move hover:bg-purple-200 dark:hover:bg-purple-800/30 transition-colors
|
package/dist/_app/version.json
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":"
|
|
1
|
+
{"version":"1760625230679"}
|
package/dist/cli/commands/ui.js
CHANGED
|
@@ -1721,8 +1721,8 @@ var init_fileStore = __esm({
|
|
|
1721
1721
|
if (!this.baseDir || this.baseDir === "." || this.baseDir === process.cwd()) {
|
|
1722
1722
|
return;
|
|
1723
1723
|
}
|
|
1724
|
-
const
|
|
1725
|
-
if (!existsSync2(
|
|
1724
|
+
const lulaConfigPath2 = join2(this.baseDir, "lula.yaml");
|
|
1725
|
+
if (!existsSync2(lulaConfigPath2)) {
|
|
1726
1726
|
return;
|
|
1727
1727
|
}
|
|
1728
1728
|
if (!existsSync2(this.controlsDir)) {
|
|
@@ -1880,6 +1880,17 @@ var init_fileStore = __esm({
|
|
|
1880
1880
|
if (!existsSync2(this.controlsDir)) {
|
|
1881
1881
|
return [];
|
|
1882
1882
|
}
|
|
1883
|
+
let controlOrder = null;
|
|
1884
|
+
try {
|
|
1885
|
+
const lulaConfigPath2 = join2(this.baseDir, "lula.yaml");
|
|
1886
|
+
if (existsSync2(lulaConfigPath2)) {
|
|
1887
|
+
const content = readFileSync2(lulaConfigPath2, "utf8");
|
|
1888
|
+
const metadata = yaml2.load(content);
|
|
1889
|
+
controlOrder = metadata?.controlOrder || null;
|
|
1890
|
+
}
|
|
1891
|
+
} catch (error) {
|
|
1892
|
+
console.error(`Failed to load lula.yaml for controlOrder (path: ${lulaConfigPath}):`, error);
|
|
1893
|
+
}
|
|
1883
1894
|
const entries = readdirSync(this.controlsDir);
|
|
1884
1895
|
const yamlFiles = entries.filter((file) => file.endsWith(".yaml"));
|
|
1885
1896
|
if (yamlFiles.length > 0) {
|
|
@@ -1898,7 +1909,11 @@ var init_fileStore = __esm({
|
|
|
1898
1909
|
}
|
|
1899
1910
|
});
|
|
1900
1911
|
const results2 = await Promise.all(promises);
|
|
1901
|
-
|
|
1912
|
+
const controls2 = results2.filter((c) => c !== null);
|
|
1913
|
+
if (controlOrder && controlOrder.length > 0) {
|
|
1914
|
+
return this.sortControlsByOrder(controls2, controlOrder);
|
|
1915
|
+
}
|
|
1916
|
+
return controls2;
|
|
1902
1917
|
}
|
|
1903
1918
|
const families = entries.filter((name) => {
|
|
1904
1919
|
const familyPath = join2(this.controlsDir, name);
|
|
@@ -1921,7 +1936,25 @@ var init_fileStore = __esm({
|
|
|
1921
1936
|
allPromises.push(...familyPromises);
|
|
1922
1937
|
}
|
|
1923
1938
|
const results = await Promise.all(allPromises);
|
|
1924
|
-
|
|
1939
|
+
const controls = results.filter((c) => c !== null);
|
|
1940
|
+
if (controlOrder && controlOrder.length > 0) {
|
|
1941
|
+
return this.sortControlsByOrder(controls, controlOrder);
|
|
1942
|
+
}
|
|
1943
|
+
return controls;
|
|
1944
|
+
}
|
|
1945
|
+
/**
|
|
1946
|
+
* Sort controls based on the provided order array
|
|
1947
|
+
*/
|
|
1948
|
+
sortControlsByOrder(controls, controlOrder) {
|
|
1949
|
+
const orderMap = /* @__PURE__ */ new Map();
|
|
1950
|
+
controlOrder.forEach((controlId, index) => {
|
|
1951
|
+
orderMap.set(controlId, index);
|
|
1952
|
+
});
|
|
1953
|
+
return controls.sort((a, b) => {
|
|
1954
|
+
const aIndex = orderMap.get(a.id) ?? Number.MAX_SAFE_INTEGER;
|
|
1955
|
+
const bIndex = orderMap.get(b.id) ?? Number.MAX_SAFE_INTEGER;
|
|
1956
|
+
return aIndex - bIndex;
|
|
1957
|
+
});
|
|
1925
1958
|
}
|
|
1926
1959
|
/**
|
|
1927
1960
|
* Load mappings from mappings directory
|
|
@@ -3041,6 +3074,7 @@ function processSpreadsheetData(rawData, headers, startRowIndex, params) {
|
|
|
3041
3074
|
continue;
|
|
3042
3075
|
}
|
|
3043
3076
|
const family = extractFamilyFromControlId(controlId);
|
|
3077
|
+
control._originalRowIndex = i;
|
|
3044
3078
|
control.family = family;
|
|
3045
3079
|
controls.push(control);
|
|
3046
3080
|
if (!families.has(family)) {
|
|
@@ -3163,6 +3197,7 @@ async function createOutputStructure(processedData, fieldSchema, params) {
|
|
|
3163
3197
|
params.controlIdField,
|
|
3164
3198
|
params.namingConvention
|
|
3165
3199
|
);
|
|
3200
|
+
const controlOrder = controls.sort((a, b) => (a._originalRowIndex || 0) - (b._originalRowIndex || 0)).map((control) => control[controlIdFieldNameClean]);
|
|
3166
3201
|
const controlSetData = {
|
|
3167
3202
|
name: params.controlSetName,
|
|
3168
3203
|
description: params.controlSetDescription,
|
|
@@ -3170,12 +3205,16 @@ async function createOutputStructure(processedData, fieldSchema, params) {
|
|
|
3170
3205
|
control_id_field: controlIdFieldNameClean,
|
|
3171
3206
|
controlCount: controls.length,
|
|
3172
3207
|
families: uniqueFamilies,
|
|
3208
|
+
controlOrder,
|
|
3173
3209
|
fieldSchema
|
|
3174
3210
|
};
|
|
3175
3211
|
writeFileSync2(join4(baseDir, "lula.yaml"), yaml4.dump(controlSetData));
|
|
3176
3212
|
const controlsDir = join4(baseDir, "controls");
|
|
3177
3213
|
const mappingsDir = join4(baseDir, "mappings");
|
|
3178
|
-
families.
|
|
3214
|
+
const sortedFamilies = Array.from(families.entries()).sort(
|
|
3215
|
+
(a, b) => a[0].localeCompare(b[0])
|
|
3216
|
+
);
|
|
3217
|
+
sortedFamilies.forEach(([family, familyControls]) => {
|
|
3179
3218
|
const familyDir = join4(controlsDir, family);
|
|
3180
3219
|
const familyMappingsDir = join4(mappingsDir, family);
|
|
3181
3220
|
if (!existsSync3(familyDir)) {
|
|
@@ -3184,7 +3223,10 @@ async function createOutputStructure(processedData, fieldSchema, params) {
|
|
|
3184
3223
|
if (!existsSync3(familyMappingsDir)) {
|
|
3185
3224
|
mkdirSync2(familyMappingsDir, { recursive: true });
|
|
3186
3225
|
}
|
|
3187
|
-
familyControls.
|
|
3226
|
+
const sortedFamilyControls = familyControls.sort(
|
|
3227
|
+
(a, b) => (a._originalRowIndex || 0) - (b._originalRowIndex || 0)
|
|
3228
|
+
);
|
|
3229
|
+
sortedFamilyControls.forEach((control) => {
|
|
3188
3230
|
const controlId = control[controlIdFieldNameClean];
|
|
3189
3231
|
if (!controlId) {
|
|
3190
3232
|
console.error("Missing control ID for control:", control);
|
|
@@ -3206,7 +3248,7 @@ async function createOutputStructure(processedData, fieldSchema, params) {
|
|
|
3206
3248
|
filteredControl.family = control.family;
|
|
3207
3249
|
}
|
|
3208
3250
|
Object.keys(control).forEach((fieldName) => {
|
|
3209
|
-
if (fieldName === "family") return;
|
|
3251
|
+
if (fieldName === "family" || fieldName === "_originalRowIndex") return;
|
|
3210
3252
|
if (params.justificationFields.includes(fieldName) && control[fieldName] !== void 0 && control[fieldName] !== null) {
|
|
3211
3253
|
justificationContents.push(control[fieldName]);
|
|
3212
3254
|
}
|
package/dist/cli/server/index.js
CHANGED
|
@@ -1703,8 +1703,8 @@ var init_fileStore = __esm({
|
|
|
1703
1703
|
if (!this.baseDir || this.baseDir === "." || this.baseDir === process.cwd()) {
|
|
1704
1704
|
return;
|
|
1705
1705
|
}
|
|
1706
|
-
const
|
|
1707
|
-
if (!existsSync2(
|
|
1706
|
+
const lulaConfigPath2 = join2(this.baseDir, "lula.yaml");
|
|
1707
|
+
if (!existsSync2(lulaConfigPath2)) {
|
|
1708
1708
|
return;
|
|
1709
1709
|
}
|
|
1710
1710
|
if (!existsSync2(this.controlsDir)) {
|
|
@@ -1862,6 +1862,17 @@ var init_fileStore = __esm({
|
|
|
1862
1862
|
if (!existsSync2(this.controlsDir)) {
|
|
1863
1863
|
return [];
|
|
1864
1864
|
}
|
|
1865
|
+
let controlOrder = null;
|
|
1866
|
+
try {
|
|
1867
|
+
const lulaConfigPath2 = join2(this.baseDir, "lula.yaml");
|
|
1868
|
+
if (existsSync2(lulaConfigPath2)) {
|
|
1869
|
+
const content = readFileSync2(lulaConfigPath2, "utf8");
|
|
1870
|
+
const metadata = yaml2.load(content);
|
|
1871
|
+
controlOrder = metadata?.controlOrder || null;
|
|
1872
|
+
}
|
|
1873
|
+
} catch (error) {
|
|
1874
|
+
console.error(`Failed to load lula.yaml for controlOrder (path: ${lulaConfigPath}):`, error);
|
|
1875
|
+
}
|
|
1865
1876
|
const entries = readdirSync(this.controlsDir);
|
|
1866
1877
|
const yamlFiles = entries.filter((file) => file.endsWith(".yaml"));
|
|
1867
1878
|
if (yamlFiles.length > 0) {
|
|
@@ -1880,7 +1891,11 @@ var init_fileStore = __esm({
|
|
|
1880
1891
|
}
|
|
1881
1892
|
});
|
|
1882
1893
|
const results2 = await Promise.all(promises);
|
|
1883
|
-
|
|
1894
|
+
const controls2 = results2.filter((c) => c !== null);
|
|
1895
|
+
if (controlOrder && controlOrder.length > 0) {
|
|
1896
|
+
return this.sortControlsByOrder(controls2, controlOrder);
|
|
1897
|
+
}
|
|
1898
|
+
return controls2;
|
|
1884
1899
|
}
|
|
1885
1900
|
const families = entries.filter((name) => {
|
|
1886
1901
|
const familyPath = join2(this.controlsDir, name);
|
|
@@ -1903,7 +1918,25 @@ var init_fileStore = __esm({
|
|
|
1903
1918
|
allPromises.push(...familyPromises);
|
|
1904
1919
|
}
|
|
1905
1920
|
const results = await Promise.all(allPromises);
|
|
1906
|
-
|
|
1921
|
+
const controls = results.filter((c) => c !== null);
|
|
1922
|
+
if (controlOrder && controlOrder.length > 0) {
|
|
1923
|
+
return this.sortControlsByOrder(controls, controlOrder);
|
|
1924
|
+
}
|
|
1925
|
+
return controls;
|
|
1926
|
+
}
|
|
1927
|
+
/**
|
|
1928
|
+
* Sort controls based on the provided order array
|
|
1929
|
+
*/
|
|
1930
|
+
sortControlsByOrder(controls, controlOrder) {
|
|
1931
|
+
const orderMap = /* @__PURE__ */ new Map();
|
|
1932
|
+
controlOrder.forEach((controlId, index) => {
|
|
1933
|
+
orderMap.set(controlId, index);
|
|
1934
|
+
});
|
|
1935
|
+
return controls.sort((a, b) => {
|
|
1936
|
+
const aIndex = orderMap.get(a.id) ?? Number.MAX_SAFE_INTEGER;
|
|
1937
|
+
const bIndex = orderMap.get(b.id) ?? Number.MAX_SAFE_INTEGER;
|
|
1938
|
+
return aIndex - bIndex;
|
|
1939
|
+
});
|
|
1907
1940
|
}
|
|
1908
1941
|
/**
|
|
1909
1942
|
* Load mappings from mappings directory
|
|
@@ -3023,6 +3056,7 @@ function processSpreadsheetData(rawData, headers, startRowIndex, params) {
|
|
|
3023
3056
|
continue;
|
|
3024
3057
|
}
|
|
3025
3058
|
const family = extractFamilyFromControlId(controlId);
|
|
3059
|
+
control._originalRowIndex = i;
|
|
3026
3060
|
control.family = family;
|
|
3027
3061
|
controls.push(control);
|
|
3028
3062
|
if (!families.has(family)) {
|
|
@@ -3145,6 +3179,7 @@ async function createOutputStructure(processedData, fieldSchema, params) {
|
|
|
3145
3179
|
params.controlIdField,
|
|
3146
3180
|
params.namingConvention
|
|
3147
3181
|
);
|
|
3182
|
+
const controlOrder = controls.sort((a, b) => (a._originalRowIndex || 0) - (b._originalRowIndex || 0)).map((control) => control[controlIdFieldNameClean]);
|
|
3148
3183
|
const controlSetData = {
|
|
3149
3184
|
name: params.controlSetName,
|
|
3150
3185
|
description: params.controlSetDescription,
|
|
@@ -3152,12 +3187,16 @@ async function createOutputStructure(processedData, fieldSchema, params) {
|
|
|
3152
3187
|
control_id_field: controlIdFieldNameClean,
|
|
3153
3188
|
controlCount: controls.length,
|
|
3154
3189
|
families: uniqueFamilies,
|
|
3190
|
+
controlOrder,
|
|
3155
3191
|
fieldSchema
|
|
3156
3192
|
};
|
|
3157
3193
|
writeFileSync2(join4(baseDir, "lula.yaml"), yaml4.dump(controlSetData));
|
|
3158
3194
|
const controlsDir = join4(baseDir, "controls");
|
|
3159
3195
|
const mappingsDir = join4(baseDir, "mappings");
|
|
3160
|
-
families.
|
|
3196
|
+
const sortedFamilies = Array.from(families.entries()).sort(
|
|
3197
|
+
(a, b) => a[0].localeCompare(b[0])
|
|
3198
|
+
);
|
|
3199
|
+
sortedFamilies.forEach(([family, familyControls]) => {
|
|
3161
3200
|
const familyDir = join4(controlsDir, family);
|
|
3162
3201
|
const familyMappingsDir = join4(mappingsDir, family);
|
|
3163
3202
|
if (!existsSync3(familyDir)) {
|
|
@@ -3166,7 +3205,10 @@ async function createOutputStructure(processedData, fieldSchema, params) {
|
|
|
3166
3205
|
if (!existsSync3(familyMappingsDir)) {
|
|
3167
3206
|
mkdirSync2(familyMappingsDir, { recursive: true });
|
|
3168
3207
|
}
|
|
3169
|
-
familyControls.
|
|
3208
|
+
const sortedFamilyControls = familyControls.sort(
|
|
3209
|
+
(a, b) => (a._originalRowIndex || 0) - (b._originalRowIndex || 0)
|
|
3210
|
+
);
|
|
3211
|
+
sortedFamilyControls.forEach((control) => {
|
|
3170
3212
|
const controlId = control[controlIdFieldNameClean];
|
|
3171
3213
|
if (!controlId) {
|
|
3172
3214
|
console.error("Missing control ID for control:", control);
|
|
@@ -3188,7 +3230,7 @@ async function createOutputStructure(processedData, fieldSchema, params) {
|
|
|
3188
3230
|
filteredControl.family = control.family;
|
|
3189
3231
|
}
|
|
3190
3232
|
Object.keys(control).forEach((fieldName) => {
|
|
3191
|
-
if (fieldName === "family") return;
|
|
3233
|
+
if (fieldName === "family" || fieldName === "_originalRowIndex") return;
|
|
3192
3234
|
if (params.justificationFields.includes(fieldName) && control[fieldName] !== void 0 && control[fieldName] !== null) {
|
|
3193
3235
|
justificationContents.push(control[fieldName]);
|
|
3194
3236
|
}
|
|
@@ -1703,8 +1703,8 @@ var init_fileStore = __esm({
|
|
|
1703
1703
|
if (!this.baseDir || this.baseDir === "." || this.baseDir === process.cwd()) {
|
|
1704
1704
|
return;
|
|
1705
1705
|
}
|
|
1706
|
-
const
|
|
1707
|
-
if (!existsSync2(
|
|
1706
|
+
const lulaConfigPath2 = join2(this.baseDir, "lula.yaml");
|
|
1707
|
+
if (!existsSync2(lulaConfigPath2)) {
|
|
1708
1708
|
return;
|
|
1709
1709
|
}
|
|
1710
1710
|
if (!existsSync2(this.controlsDir)) {
|
|
@@ -1862,6 +1862,17 @@ var init_fileStore = __esm({
|
|
|
1862
1862
|
if (!existsSync2(this.controlsDir)) {
|
|
1863
1863
|
return [];
|
|
1864
1864
|
}
|
|
1865
|
+
let controlOrder = null;
|
|
1866
|
+
try {
|
|
1867
|
+
const lulaConfigPath2 = join2(this.baseDir, "lula.yaml");
|
|
1868
|
+
if (existsSync2(lulaConfigPath2)) {
|
|
1869
|
+
const content = readFileSync2(lulaConfigPath2, "utf8");
|
|
1870
|
+
const metadata = yaml2.load(content);
|
|
1871
|
+
controlOrder = metadata?.controlOrder || null;
|
|
1872
|
+
}
|
|
1873
|
+
} catch (error) {
|
|
1874
|
+
console.error(`Failed to load lula.yaml for controlOrder (path: ${lulaConfigPath}):`, error);
|
|
1875
|
+
}
|
|
1865
1876
|
const entries = readdirSync(this.controlsDir);
|
|
1866
1877
|
const yamlFiles = entries.filter((file) => file.endsWith(".yaml"));
|
|
1867
1878
|
if (yamlFiles.length > 0) {
|
|
@@ -1880,7 +1891,11 @@ var init_fileStore = __esm({
|
|
|
1880
1891
|
}
|
|
1881
1892
|
});
|
|
1882
1893
|
const results2 = await Promise.all(promises);
|
|
1883
|
-
|
|
1894
|
+
const controls2 = results2.filter((c) => c !== null);
|
|
1895
|
+
if (controlOrder && controlOrder.length > 0) {
|
|
1896
|
+
return this.sortControlsByOrder(controls2, controlOrder);
|
|
1897
|
+
}
|
|
1898
|
+
return controls2;
|
|
1884
1899
|
}
|
|
1885
1900
|
const families = entries.filter((name) => {
|
|
1886
1901
|
const familyPath = join2(this.controlsDir, name);
|
|
@@ -1903,7 +1918,25 @@ var init_fileStore = __esm({
|
|
|
1903
1918
|
allPromises.push(...familyPromises);
|
|
1904
1919
|
}
|
|
1905
1920
|
const results = await Promise.all(allPromises);
|
|
1906
|
-
|
|
1921
|
+
const controls = results.filter((c) => c !== null);
|
|
1922
|
+
if (controlOrder && controlOrder.length > 0) {
|
|
1923
|
+
return this.sortControlsByOrder(controls, controlOrder);
|
|
1924
|
+
}
|
|
1925
|
+
return controls;
|
|
1926
|
+
}
|
|
1927
|
+
/**
|
|
1928
|
+
* Sort controls based on the provided order array
|
|
1929
|
+
*/
|
|
1930
|
+
sortControlsByOrder(controls, controlOrder) {
|
|
1931
|
+
const orderMap = /* @__PURE__ */ new Map();
|
|
1932
|
+
controlOrder.forEach((controlId, index) => {
|
|
1933
|
+
orderMap.set(controlId, index);
|
|
1934
|
+
});
|
|
1935
|
+
return controls.sort((a, b) => {
|
|
1936
|
+
const aIndex = orderMap.get(a.id) ?? Number.MAX_SAFE_INTEGER;
|
|
1937
|
+
const bIndex = orderMap.get(b.id) ?? Number.MAX_SAFE_INTEGER;
|
|
1938
|
+
return aIndex - bIndex;
|
|
1939
|
+
});
|
|
1907
1940
|
}
|
|
1908
1941
|
/**
|
|
1909
1942
|
* Load mappings from mappings directory
|
|
@@ -3023,6 +3056,7 @@ function processSpreadsheetData(rawData, headers, startRowIndex, params) {
|
|
|
3023
3056
|
continue;
|
|
3024
3057
|
}
|
|
3025
3058
|
const family = extractFamilyFromControlId(controlId);
|
|
3059
|
+
control._originalRowIndex = i;
|
|
3026
3060
|
control.family = family;
|
|
3027
3061
|
controls.push(control);
|
|
3028
3062
|
if (!families.has(family)) {
|
|
@@ -3145,6 +3179,7 @@ async function createOutputStructure(processedData, fieldSchema, params) {
|
|
|
3145
3179
|
params.controlIdField,
|
|
3146
3180
|
params.namingConvention
|
|
3147
3181
|
);
|
|
3182
|
+
const controlOrder = controls.sort((a, b) => (a._originalRowIndex || 0) - (b._originalRowIndex || 0)).map((control) => control[controlIdFieldNameClean]);
|
|
3148
3183
|
const controlSetData = {
|
|
3149
3184
|
name: params.controlSetName,
|
|
3150
3185
|
description: params.controlSetDescription,
|
|
@@ -3152,12 +3187,16 @@ async function createOutputStructure(processedData, fieldSchema, params) {
|
|
|
3152
3187
|
control_id_field: controlIdFieldNameClean,
|
|
3153
3188
|
controlCount: controls.length,
|
|
3154
3189
|
families: uniqueFamilies,
|
|
3190
|
+
controlOrder,
|
|
3155
3191
|
fieldSchema
|
|
3156
3192
|
};
|
|
3157
3193
|
writeFileSync2(join4(baseDir, "lula.yaml"), yaml4.dump(controlSetData));
|
|
3158
3194
|
const controlsDir = join4(baseDir, "controls");
|
|
3159
3195
|
const mappingsDir = join4(baseDir, "mappings");
|
|
3160
|
-
families.
|
|
3196
|
+
const sortedFamilies = Array.from(families.entries()).sort(
|
|
3197
|
+
(a, b) => a[0].localeCompare(b[0])
|
|
3198
|
+
);
|
|
3199
|
+
sortedFamilies.forEach(([family, familyControls]) => {
|
|
3161
3200
|
const familyDir = join4(controlsDir, family);
|
|
3162
3201
|
const familyMappingsDir = join4(mappingsDir, family);
|
|
3163
3202
|
if (!existsSync3(familyDir)) {
|
|
@@ -3166,7 +3205,10 @@ async function createOutputStructure(processedData, fieldSchema, params) {
|
|
|
3166
3205
|
if (!existsSync3(familyMappingsDir)) {
|
|
3167
3206
|
mkdirSync2(familyMappingsDir, { recursive: true });
|
|
3168
3207
|
}
|
|
3169
|
-
familyControls.
|
|
3208
|
+
const sortedFamilyControls = familyControls.sort(
|
|
3209
|
+
(a, b) => (a._originalRowIndex || 0) - (b._originalRowIndex || 0)
|
|
3210
|
+
);
|
|
3211
|
+
sortedFamilyControls.forEach((control) => {
|
|
3170
3212
|
const controlId = control[controlIdFieldNameClean];
|
|
3171
3213
|
if (!controlId) {
|
|
3172
3214
|
console.error("Missing control ID for control:", control);
|
|
@@ -3188,7 +3230,7 @@ async function createOutputStructure(processedData, fieldSchema, params) {
|
|
|
3188
3230
|
filteredControl.family = control.family;
|
|
3189
3231
|
}
|
|
3190
3232
|
Object.keys(control).forEach((fieldName) => {
|
|
3191
|
-
if (fieldName === "family") return;
|
|
3233
|
+
if (fieldName === "family" || fieldName === "_originalRowIndex") return;
|
|
3192
3234
|
if (params.justificationFields.includes(fieldName) && control[fieldName] !== void 0 && control[fieldName] !== null) {
|
|
3193
3235
|
justificationContents.push(control[fieldName]);
|
|
3194
3236
|
}
|
|
@@ -124,8 +124,8 @@ var FileStore = class {
|
|
|
124
124
|
if (!this.baseDir || this.baseDir === "." || this.baseDir === process.cwd()) {
|
|
125
125
|
return;
|
|
126
126
|
}
|
|
127
|
-
const
|
|
128
|
-
if (!existsSync2(
|
|
127
|
+
const lulaConfigPath2 = join2(this.baseDir, "lula.yaml");
|
|
128
|
+
if (!existsSync2(lulaConfigPath2)) {
|
|
129
129
|
return;
|
|
130
130
|
}
|
|
131
131
|
if (!existsSync2(this.controlsDir)) {
|
|
@@ -283,6 +283,17 @@ var FileStore = class {
|
|
|
283
283
|
if (!existsSync2(this.controlsDir)) {
|
|
284
284
|
return [];
|
|
285
285
|
}
|
|
286
|
+
let controlOrder = null;
|
|
287
|
+
try {
|
|
288
|
+
const lulaConfigPath2 = join2(this.baseDir, "lula.yaml");
|
|
289
|
+
if (existsSync2(lulaConfigPath2)) {
|
|
290
|
+
const content = readFileSync2(lulaConfigPath2, "utf8");
|
|
291
|
+
const metadata = yaml2.load(content);
|
|
292
|
+
controlOrder = metadata?.controlOrder || null;
|
|
293
|
+
}
|
|
294
|
+
} catch (error) {
|
|
295
|
+
console.error(`Failed to load lula.yaml for controlOrder (path: ${lulaConfigPath}):`, error);
|
|
296
|
+
}
|
|
286
297
|
const entries = readdirSync(this.controlsDir);
|
|
287
298
|
const yamlFiles = entries.filter((file) => file.endsWith(".yaml"));
|
|
288
299
|
if (yamlFiles.length > 0) {
|
|
@@ -301,7 +312,11 @@ var FileStore = class {
|
|
|
301
312
|
}
|
|
302
313
|
});
|
|
303
314
|
const results2 = await Promise.all(promises);
|
|
304
|
-
|
|
315
|
+
const controls2 = results2.filter((c) => c !== null);
|
|
316
|
+
if (controlOrder && controlOrder.length > 0) {
|
|
317
|
+
return this.sortControlsByOrder(controls2, controlOrder);
|
|
318
|
+
}
|
|
319
|
+
return controls2;
|
|
305
320
|
}
|
|
306
321
|
const families = entries.filter((name) => {
|
|
307
322
|
const familyPath = join2(this.controlsDir, name);
|
|
@@ -324,7 +339,25 @@ var FileStore = class {
|
|
|
324
339
|
allPromises.push(...familyPromises);
|
|
325
340
|
}
|
|
326
341
|
const results = await Promise.all(allPromises);
|
|
327
|
-
|
|
342
|
+
const controls = results.filter((c) => c !== null);
|
|
343
|
+
if (controlOrder && controlOrder.length > 0) {
|
|
344
|
+
return this.sortControlsByOrder(controls, controlOrder);
|
|
345
|
+
}
|
|
346
|
+
return controls;
|
|
347
|
+
}
|
|
348
|
+
/**
|
|
349
|
+
* Sort controls based on the provided order array
|
|
350
|
+
*/
|
|
351
|
+
sortControlsByOrder(controls, controlOrder) {
|
|
352
|
+
const orderMap = /* @__PURE__ */ new Map();
|
|
353
|
+
controlOrder.forEach((controlId, index) => {
|
|
354
|
+
orderMap.set(controlId, index);
|
|
355
|
+
});
|
|
356
|
+
return controls.sort((a, b) => {
|
|
357
|
+
const aIndex = orderMap.get(a.id) ?? Number.MAX_SAFE_INTEGER;
|
|
358
|
+
const bIndex = orderMap.get(b.id) ?? Number.MAX_SAFE_INTEGER;
|
|
359
|
+
return aIndex - bIndex;
|
|
360
|
+
});
|
|
328
361
|
}
|
|
329
362
|
/**
|
|
330
363
|
* Load mappings from mappings directory
|
|
@@ -267,6 +267,7 @@ function processSpreadsheetData(rawData, headers, startRowIndex, params) {
|
|
|
267
267
|
continue;
|
|
268
268
|
}
|
|
269
269
|
const family = extractFamilyFromControlId(controlId);
|
|
270
|
+
control._originalRowIndex = i;
|
|
270
271
|
control.family = family;
|
|
271
272
|
controls.push(control);
|
|
272
273
|
if (!families.has(family)) {
|
|
@@ -389,6 +390,7 @@ async function createOutputStructure(processedData, fieldSchema, params) {
|
|
|
389
390
|
params.controlIdField,
|
|
390
391
|
params.namingConvention
|
|
391
392
|
);
|
|
393
|
+
const controlOrder = controls.sort((a, b) => (a._originalRowIndex || 0) - (b._originalRowIndex || 0)).map((control) => control[controlIdFieldNameClean]);
|
|
392
394
|
const controlSetData = {
|
|
393
395
|
name: params.controlSetName,
|
|
394
396
|
description: params.controlSetDescription,
|
|
@@ -396,12 +398,16 @@ async function createOutputStructure(processedData, fieldSchema, params) {
|
|
|
396
398
|
control_id_field: controlIdFieldNameClean,
|
|
397
399
|
controlCount: controls.length,
|
|
398
400
|
families: uniqueFamilies,
|
|
401
|
+
controlOrder,
|
|
399
402
|
fieldSchema
|
|
400
403
|
};
|
|
401
404
|
writeFileSync(join2(baseDir, "lula.yaml"), yaml4.dump(controlSetData));
|
|
402
405
|
const controlsDir = join2(baseDir, "controls");
|
|
403
406
|
const mappingsDir = join2(baseDir, "mappings");
|
|
404
|
-
families.
|
|
407
|
+
const sortedFamilies = Array.from(families.entries()).sort(
|
|
408
|
+
(a, b) => a[0].localeCompare(b[0])
|
|
409
|
+
);
|
|
410
|
+
sortedFamilies.forEach(([family, familyControls]) => {
|
|
405
411
|
const familyDir = join2(controlsDir, family);
|
|
406
412
|
const familyMappingsDir = join2(mappingsDir, family);
|
|
407
413
|
if (!existsSync(familyDir)) {
|
|
@@ -410,7 +416,10 @@ async function createOutputStructure(processedData, fieldSchema, params) {
|
|
|
410
416
|
if (!existsSync(familyMappingsDir)) {
|
|
411
417
|
mkdirSync(familyMappingsDir, { recursive: true });
|
|
412
418
|
}
|
|
413
|
-
familyControls.
|
|
419
|
+
const sortedFamilyControls = familyControls.sort(
|
|
420
|
+
(a, b) => (a._originalRowIndex || 0) - (b._originalRowIndex || 0)
|
|
421
|
+
);
|
|
422
|
+
sortedFamilyControls.forEach((control) => {
|
|
414
423
|
const controlId = control[controlIdFieldNameClean];
|
|
415
424
|
if (!controlId) {
|
|
416
425
|
console.error("Missing control ID for control:", control);
|
|
@@ -432,7 +441,7 @@ async function createOutputStructure(processedData, fieldSchema, params) {
|
|
|
432
441
|
filteredControl.family = control.family;
|
|
433
442
|
}
|
|
434
443
|
Object.keys(control).forEach((fieldName) => {
|
|
435
|
-
if (fieldName === "family") return;
|
|
444
|
+
if (fieldName === "family" || fieldName === "_originalRowIndex") return;
|
|
436
445
|
if (params.justificationFields.includes(fieldName) && control[fieldName] !== void 0 && control[fieldName] !== null) {
|
|
437
446
|
justificationContents.push(control[fieldName]);
|
|
438
447
|
}
|