lula2 0.3.2 → 0.3.3
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.BeyRWDYp.css +1 -0
- package/dist/_app/immutable/chunks/{C57TYu27.js → BTTmO90G.js} +1 -1
- package/dist/_app/immutable/chunks/{DY3-lqhI.js → C2RwV308.js} +1 -1
- package/dist/_app/immutable/chunks/C9p1zaS8.js +2 -0
- package/dist/_app/immutable/chunks/{1spjHGNy.js → DkTmghUd.js} +1 -1
- package/dist/_app/immutable/chunks/{Dfk9QG8E.js → Dz_3mPpR.js} +1 -1
- package/dist/_app/immutable/chunks/{CNOPXlDW.js → EhHGBAhB.js} +1 -1
- package/dist/_app/immutable/chunks/{152nb-LI.js → ZLxiKtk1.js} +1 -1
- package/dist/_app/immutable/chunks/{BtuEtkd3.js → cvagBx-i.js} +1 -1
- package/dist/_app/immutable/chunks/z5X83jua.js +3 -0
- package/dist/_app/immutable/entry/app.DlQKiOqI.js +2 -0
- package/dist/_app/immutable/entry/start.CuPBZjFh.js +1 -0
- package/dist/_app/immutable/nodes/{0.Bw6pYYFU.js → 0.BebGxk-A.js} +1 -1
- package/dist/_app/immutable/nodes/{1.DbCtPiQe.js → 1.DfUs1HjM.js} +1 -1
- package/dist/_app/immutable/nodes/{2.CWnpuE-a.js → 2.CrOeZTVE.js} +1 -1
- package/dist/_app/immutable/nodes/{3.hZ7uQfPx.js → 3.D6SgInYh.js} +1 -1
- package/dist/_app/immutable/nodes/4.Be0URtGy.js +11 -0
- package/dist/_app/version.json +1 -1
- package/dist/cli/commands/ui.js +7 -9
- package/dist/cli/server/index.js +7 -9
- package/dist/cli/server/server.js +7 -9
- package/dist/cli/server/serverState.js +3 -1
- package/dist/cli/server/spreadsheetRoutes.js +4 -8
- package/dist/cli/server/websocketServer.js +7 -9
- package/dist/index.html +10 -10
- package/dist/index.js +7 -9
- package/package.json +2 -2
- package/src/lib/components/controls/tabs/CustomFieldsTab.svelte +1 -1
- package/src/lib/components/controls/tabs/TimelineTab.svelte +1 -1
- package/src/lib/components/controls/utils/ProcessedTextRenderer.svelte +1 -1
- package/src/lib/components/forms/DynamicControlForm.svelte +2 -2
- package/src/lib/components/setup/ExistingControlSets.svelte +0 -1
- package/src/lib/components/setup/SpreadsheetImport.svelte +104 -105
- package/dist/_app/immutable/assets/0.CLKu6Q8_.css +0 -1
- package/dist/_app/immutable/chunks/C113Bo4B.js +0 -2
- package/dist/_app/immutable/chunks/DVAnYkTd.js +0 -3
- package/dist/_app/immutable/entry/app.D0O4A2k5.js +0 -2
- package/dist/_app/immutable/entry/start.DtHY97nW.js +0 -1
- package/dist/_app/immutable/nodes/4.j9xYp9Vt.js +0 -12
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import"../chunks/DsnmJJEf.js";import{i as Ye}from"../chunks/DkTmghUd.js";import{p as We,am as Oe,d as o,T as C,G as te,h as e,an as ir,ao as Ne,c as a,n as Se,r,b as d,e as Xe,J as H,K as E,ap as Nr,f as h,s as i,k as _,aq as p,a as we,ar as _r,aj as Fe,a0 as Ae,ak as wr,al as lr,as as kr,o as lt,W as st}from"../chunks/C9p1zaS8.js";import{l as sr,p as Je,i as A,a as it,s as nt}from"../chunks/C2RwV308.js";import{a as Br,s as ie,r as jr,e as xe,i as Ce,b as Ue,c as Lr,d as Cr,f as Dr,w as Sr}from"../chunks/ZLxiKtk1.js";import{g as dt}from"../chunks/z5X83jua.js";function vt(me){return function(...U){var P=U[0];return P.stopPropagation(),me?.apply(this,U)}}var ct=Ne("<title> </title>"),gt=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 pt(me,U){const P=sr(U,["children","$$slots","$$events","$$legacy"]),ne=sr(P,["size","title"]);We(U,!1);const I=C(),T=C();let N=Je(U,"size",8,16),w=Je(U,"title",8,void 0);Oe(()=>(te(P),te(w())),()=>{o(I,P["aria-label"]||P["aria-labelledby"]||w())}),Oe(()=>(e(I),te(P)),()=>{o(T,{"aria-hidden":e(I)?void 0:!0,role:e(I)?"img":void 0,focusable:Number(P.tabindex)===0?!0:void 0})}),ir(),Ye();var m=gt();Br(m,()=>({xmlns:"http://www.w3.org/2000/svg",viewBox:"0 0 32 32",fill:"currentColor",preserveAspectRatio:"xMidYMid meet",width:N(),height:N(),...e(T),...ne}));var S=a(m);{var ue=J=>{var F=ct(),c=a(F,!0);r(F),H(()=>E(c,w())),d(J,F)};A(S,J=>{w()&&J(ue)})}Se(2),r(m),d(me,m),Xe()}var ut=Ne("<title> </title>"),bt=Ne('<svg><!><path d="M10 6H14V10H10zM18 6H22V10H18zM10 14H14V18H10zM18 14H22V18H18zM10 22H14V26H10zM18 22H22V26H18z"></path></svg>');function $r(me,U){const P=sr(U,["children","$$slots","$$events","$$legacy"]),ne=sr(P,["size","title"]);We(U,!1);const I=C(),T=C();let N=Je(U,"size",8,16),w=Je(U,"title",8,void 0);Oe(()=>(te(P),te(w())),()=>{o(I,P["aria-label"]||P["aria-labelledby"]||w())}),Oe(()=>(e(I),te(P)),()=>{o(T,{"aria-hidden":e(I)?void 0:!0,role:e(I)?"img":void 0,focusable:Number(P.tabindex)===0?!0:void 0})}),ir(),Ye();var m=bt();Br(m,()=>({xmlns:"http://www.w3.org/2000/svg",viewBox:"0 0 32 32",fill:"currentColor",preserveAspectRatio:"xMidYMid meet",width:N(),height:N(),...e(T),...ne}));var S=a(m);{var ue=J=>{var F=ut(),c=a(F,!0);r(F),H(()=>E(c,w())),d(J,F)};A(S,J=>{w()&&J(ue)})}Se(),r(m),d(me,m),Xe()}var ft=h('<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>'),xt=h('<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=h('<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>'),ht=h("<option> </option>"),yt=h("<option> </option>"),_t=h("<option> </option>"),wt=h('<span class="ml-auto text-xs text-blue-600 dark:text-blue-400">ID</span>'),kt=h('<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>'),Ct=h('<p class="text-xs text-gray-400 dark:text-gray-500 text-center py-4">No excluded fields</p>'),St=h('<div draggable="true" role="button" tabindex="0"><!> <span class="truncate"> </span></div>'),$t=h('<p class="text-xs text-gray-400 dark:text-gray-500 text-center py-4">Drop fields here</p>'),Dt=h('<div draggable="true" role="button" tabindex="0"><!> <span class="truncate"> </span></div>'),Mt=h('<p class="text-xs text-gray-400 dark:text-gray-500 text-center py-4">Drop fields here</p>'),zt=h('<div draggable="true" role="button" tabindex="0"><!> <span class="truncate"> </span></div>'),At=h('<p class="text-xs text-gray-400 dark:text-gray-500 text-center py-4">Drop fields here</p>'),Ft=h('<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>'),Ot=h('<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>'),Pt=h('<th class="px-4 py-2"> </th>'),Ht=h('<td class="px-4 py-2"> </td>'),Et=h('<tr class="bg-white border-b dark:bg-gray-800 dark:border-gray-700"></tr>'),It=h('<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>'),Tt=h('<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>'),Vt=h(`<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
|
+
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=h('<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 Lt(me,U){We(U,!1);const P=C(),ne=Nr();let I=C(null),T=C(""),N=C(""),w=C([]),m=C([]),S=C([]),ue=C(0),J=C([]),F=C([]),c=C(new Map),be=C(1),Z=C(""),B=C(""),ae=C(""),Q=C(!1),oe=C(""),fe=C(""),de=C(!1),ve=C(!1),M=C(null),K=C(null),le=C(null);function u(){o(m,[]),o(S,[]),o(ue,0),e(c).clear(),o(c,new Map),o(F,[]),o(Z,""),o(oe,""),o(fe,""),o(M,null),o(K,null),o(le,null)}function b(l){l.preventDefault(),o(ve,!0)}function $(){o(ve,!1)}function j(l){l.preventDefault(),o(ve,!1);const n=l.dataTransfer?.files;n&&n.length>0&&Y(n[0])}function V(l){const n=l.target;n.files&&n.files.length>0&&Y(n.files[0])}async function Y(l){u(),o(T,l.name),o(I,l),o(oe,""),o(Q,!0),o(B,e(T).replace(/\.[^.]+$/,"").replace(/[-_]/g," ")),o(ae,`Imported from ${e(T)}`);try{const n=new FormData;n.append("file",l);const D=await fetch("/api/parse-excel",{method:"POST",body:n});if(!D.ok){const y=await D.json();throw new Error(y.error||"Failed to parse file")}const k=await D.json();o(w,k.sheets||[]),o(N,k.selectedSheet||e(w)[0]),o(J,k.rowPreviews||[]),e(J).length>0&&e(be)===1&&o(be,e(J)[0].row),await ce(),o(de,!0)}catch(n){o(oe,"Error reading file: "+n.message)}finally{o(Q,!1)}}async function ce(){if(!(!e(I)||!e(N))){o(Q,!0),e(c).clear(),o(c,new Map),o(Z,"");try{const l=new FormData;l.append("file",e(I)),l.append("sheetName",e(N)),l.append("headerRow",e(be).toString());const n=await fetch("/api/parse-excel-sheet",{method:"POST",body:l});if(!n.ok){const k=await n.json();throw new Error(k.error||"Failed to parse sheet")}const D=await n.json();if(o(m,D.fields||[]),o(S,D.sampleData||[]),o(ue,D.controlCount||0),e(m).forEach((k,y)=>{const g=k.toLowerCase();let L="custom",R="text";g.includes("implementation")||g.includes("status")||g.includes("narrative")||g.includes("guidance")?L="implementation":(g.includes("id")||g.includes("title")||g.includes("family")||g.includes("cci")||g.includes("control")||g.includes("acronym"))&&(L="overview"),g.includes("description")||g.includes("narrative")||g.includes("guidance")||g.includes("statement")?R="textarea":g.includes("status")||g.includes("type")||g.includes("designation")?R="select":g.includes("date")?R="date":(g.includes("count")||g.includes("number"))&&(R="number"),e(c).set(k,{originalName:k,tab:L,displayOrder:y,fieldType:R,required:g.includes("id")||g.includes("title")})}),o(c,e(c)),e(Z)&&!e(m).includes(e(Z))&&o(Z,""),!e(Z)&&e(m).includes("AP Acronym")){const k=!e(S).length||e(S).every(ee=>!ee["AP Acronym"]||String(ee["AP Acronym"]).length<25),y=e(S).map(ee=>ee["AP Acronym"]).filter(ee=>ee!=null&&ee!==""&&String(ee).trim()!==""),g=new Set(y),L=!y.length||g.size===y.length,R=y.length>0;k&&L&&R&&o(Z,"AP Acronym")}}catch(l){o(oe,"Error loading sheet data: "+l.message)}finally{o(Q,!1)}}}function Pe(l){if(!l)return l;let n=l.trim().replace(/\r?\n/g," ").replace(/\s+/g," ").trim();return $e(n)}function $e(l){return l.replace(/\W+/g," ").split(/ |\s/).map(n=>n.toLowerCase()).join("-")}function se(l,n){o(M,n),l.dataTransfer&&(l.dataTransfer.effectAllowed="move",l.dataTransfer.setData("text/plain",n))}function z(){o(M,null),o(K,null),o(le,null)}function ge(l,n){l.preventDefault(),o(K,n),l.dataTransfer&&(l.dataTransfer.dropEffect="move")}function pe(){o(K,null)}function ye(l,n,D){if(l.preventDefault(),e(M)&&e(c).has(e(M))){const k=e(c).get(e(M));if(n==="mappings")e(F).includes(e(M))||o(F,[...e(F),e(M)]);else{if(k.tab=n,D!==void 0&&n!==null){const g=Array.from(e(c).entries()).filter(([L,R])=>R.tab===n).sort((L,R)=>L[1].displayOrder-R[1].displayOrder).filter(([L])=>L!==e(M));g.splice(D,0,[e(M),k]),g.forEach(([L,R],ee)=>{R.displayOrder=ee,e(c).set(L,R)})}else if(n!==null){const y=Math.max(0,...Array.from(e(c).values()).filter(g=>g.tab===n).map(g=>g.displayOrder));k.displayOrder=y+1}e(c).set(e(M),k),o(c,e(c))}}o(M,null),o(K,null)}function De(l,n){l.preventDefault(),l.stopPropagation(),o(le,n)}function Me(){o(le,null)}function ze(l,n,D){if(l.preventDefault(),l.stopPropagation(),e(M)&&e(M)!==n){const y=Array.from(e(c).entries()).filter(([g,L])=>L.tab===D).sort((g,L)=>g[1].displayOrder-L[1].displayOrder).findIndex(([g])=>g===n);y!==-1&&ye(l,D,y)}o(le,null)}async function nr(){if(!(!e(I)||!e(T))){if(!e(Z)){o(oe,"Please select a Control ID field before importing"),o(fe,"");return}if(!e(B)||e(B).trim()===""){o(oe,"Please enter a Control Set Name before importing"),o(fe,"");return}o(Q,!0),o(oe,""),o(fe,"");try{const l=new FormData;l.append("file",e(I),e(T)),l.append("controlIdField",e(Z)),l.append("startRow",e(be).toString()),l.append("namingConvention","kebab-case"),l.append("skipEmpty","true"),l.append("skipEmptyRows","true"),l.append("controlSetName",e(B)||e(T).replace(/\.[^.]+$/,"")),l.append("controlSetDescription",e(ae)||`Imported from ${e(T)}`);const n=Array.from(e(c).entries()).filter(([y,g])=>g.tab!==null).map(([y,g])=>({fieldName:Pe(y),...g}));l.append("fieldSchema",JSON.stringify(n)),l.append("justificationFields",JSON.stringify(e(F).map(y=>Pe(y))));const D=await fetch("/api/import-spreadsheet",{method:"POST",body:l});if(!D.ok){const y=await D.json();throw new Error(y.error||"Import failed")}const k=await D.json();o(fe,`Successfully imported ${k.controlCount} controls into ${k.families.length} families`),ne("created",{path:k.outputDir})}catch(l){o(oe,"Error importing spreadsheet: "+l.message)}finally{o(Q,!1)}}}Oe(()=>e(m),()=>{o(P,e(m).filter(l=>e(m).includes(l)))}),ir(),Ye();var He=jt(),ke=a(He),Ee=a(ke),Be=a(Ee),Ke=a(Be);pt(Ke,{class:"w-8 h-8 mb-4 text-gray-500 dark:text-gray-400"}),Se(4),r(Be);var Ze=i(Be,2);r(Ee),r(ke);var Ge=i(ke,2);{var dr=l=>{var n=ft(),D=a(n),k=i(a(D),2),y=i(a(k)),g=i(y),L=i(a(g)),R=i(L,2),ee=i(R,2);r(g),r(k),r(D),r(n),H(()=>{E(y,` ${e(T)??""} `),E(L,` ${e(w),_(()=>e(w).length)??""} | `),E(R,` ${e(m),_(()=>e(m).length)??""} | `),E(ee,` ${e(ue)??""}`)}),d(l,n)};A(Ge,l=>{e(T)&&l(dr)})}var Qe=i(Ge,2);{var er=l=>{var n=xt(),D=a(n),k=i(a(D),2),y=a(k,!0);r(k),r(D),r(n),H(()=>E(y,e(oe))),d(l,n)};A(Qe,l=>{e(oe)&&l(er)})}var rr=i(Qe,2);{var vr=l=>{var n=mt(),D=a(n),k=i(a(D),2),y=a(k,!0);r(k),r(D),r(n),H(()=>E(y,e(fe))),d(l,n)};A(rr,l=>{e(fe)&&l(vr)})}var cr=i(rr,2);{var G=l=>{var n=Vt(),D=we(n),k=i(a(D),2),y=a(k),g=i(a(y),2);jr(g),Se(2),r(y);var L=i(y,2),R=i(a(L),2);jr(R),Se(2),r(L),r(k);var ee=i(k,2),gr=a(ee),tr=i(a(gr),2);H(()=>{e(N),_r(()=>{e(w)})}),xe(tr,5,()=>e(w),Ce,(t,s)=>{var f=ht(),x=a(f,!0);r(f);var v={};H(()=>{E(x,e(s)),v!==(v=e(s))&&(f.value=(f.__value=e(s))??"")}),d(t,f)}),r(tr),Se(2),r(gr);var pr=i(gr,2),ar=i(a(pr),2);H(()=>{e(be),_r(()=>{e(J)})}),xe(ar,5,()=>e(J),Ce,(t,s)=>{var f=yt(),x=a(f);r(f);var v={};H(()=>{E(x,`Row ${e(s),_(()=>e(s).row)??""}: ${e(s),_(()=>e(s).preview)??""}`),v!==(v=(e(s),_(()=>e(s).row)))&&(f.value=(f.__value=(e(s),_(()=>e(s).row)))??"")}),d(t,f)}),r(ar),Se(2),r(pr);var Mr=i(pr,2),or=i(a(Mr),2);H(()=>{e(Z),_r(()=>{e(m),e(S)})});var ur=a(or);ur.value=ur.__value="";var Zr=i(ur);xe(Zr,1,()=>e(m),Ce,(t,s)=>{const f=Ae(()=>(e(S),e(s),_(()=>e(S).length>0&&e(S)[0][e(s)]?String(e(S)[0][e(s)]).slice(0,30):""))),x=Ae(()=>(e(S),e(s),_(()=>!e(S).length||e(S).every(re=>!re[e(s)]||String(re[e(s)]).length<25)))),v=Ae(()=>(e(S),e(s),_(()=>e(S).map(re=>re[e(s)]).filter(re=>re!=null&&re!==""&&String(re).trim()!=="")))),q=Ae(()=>new Set(e(v))),W=Ae(()=>(te(e(v)),te(e(q)),_(()=>!e(v).length||e(q).size===e(v).length))),X=Ae(()=>(te(e(v)),_(()=>e(v).length>0)));var O=Fe(),he=we(O);{var qe=re=>{var _e=_t(),yr=a(_e);r(_e);var Vr={};H(()=>{E(yr,`${e(s)??""}${e(f)?` (e.g., ${e(f)})`:""}`),Vr!==(Vr=e(s))&&(_e.value=(_e.__value=e(s))??"")}),d(re,_e)};A(he,re=>{e(x)&&e(W)&&e(X)&&re(qe)})}d(t,O)}),r(or),Se(2),r(Mr),r(ee),r(D);var br=i(D,2),zr=i(a(br),4),fr=a(zr),Ie=i(a(fr),2),Ar=a(Ie);xe(Ar,1,()=>(e(m),e(c),_(()=>e(m).filter(t=>!e(c).get(t)||e(c).get(t)?.tab===null))),t=>t,(t,s)=>{var f=kt(),x=i(a(f),2),v=a(x,!0);r(x);var q=i(x,2);{var W=X=>{var O=wt();d(X,O)};A(q,X=>{e(s)===e(Z)&&X(W)})}r(f),H(()=>{Ue(f,"aria-label",`Drag ${e(s)??""} field`),E(v,e(s))}),p("dragstart",f,X=>se(X,e(s))),p("dragend",f,z),d(t,f)});var Rr=i(Ar,2);{var qr=t=>{var s=Ct();d(t,s)};A(Rr,t=>{e(m),e(c),_(()=>e(m).filter(s=>!e(c).get(s)||e(c).get(s)?.tab===null).length===0)&&t(qr)})}r(Ie),r(fr);var xr=i(fr,2),Te=i(a(xr),2),Fr=a(Te);xe(Fr,3,()=>(e(c),_(()=>Array.from(e(c).entries()).filter(([t,s])=>s.tab==="overview").sort((t,s)=>t[1].displayOrder-s[1].displayOrder))),([t,s])=>t,(t,s)=>{var f=wr(()=>kr(e(s),2));let x=()=>e(f)[0];var v=St(),q=a(v);$r(q,{class:"w-3 h-3 mr-2 flex-shrink-0"});var W=i(q,2),X=a(W,!0);r(W),r(v),H(()=>{Ue(v,"aria-label",`${x()??""} field in Overview tab`),ie(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
|
+
${e(le)===x()&&e(M)!==x()?"border-t-2 border-blue-500":""}`),E(X,x())}),p("dragstart",v,O=>se(O,x())),p("dragend",v,z),p("dragover",v,O=>De(O,x())),p("dragleave",v,Me),p("drop",v,O=>ze(O,x(),"overview")),d(t,v)});var Ur=i(Fr,2);{var Jr=t=>{var s=$t();d(t,s)};A(Ur,t=>{e(c),_(()=>Array.from(e(c).entries()).filter(([s,f])=>f.tab==="overview").length===0)&&t(Jr)})}r(Te),r(xr);var mr=i(xr,2),Ve=i(a(mr),2),Or=a(Ve);xe(Or,3,()=>(e(c),_(()=>Array.from(e(c).entries()).filter(([t,s])=>s.tab==="implementation").sort((t,s)=>t[1].displayOrder-s[1].displayOrder))),([t,s])=>t,(t,s)=>{var f=wr(()=>kr(e(s),2));let x=()=>e(f)[0];var v=Dt(),q=a(v);$r(q,{class:"w-3 h-3 mr-2 flex-shrink-0"});var W=i(q,2),X=a(W,!0);r(W),r(v),H(()=>{Ue(v,"aria-label",`${x()??""} field in Implementation tab`),ie(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
|
+
${e(le)===x()&&e(M)!==x()?"border-t-2 border-green-500":""}`),E(X,x())}),p("dragstart",v,O=>se(O,x())),p("dragend",v,z),p("dragover",v,O=>De(O,x())),p("dragleave",v,Me),p("drop",v,O=>ze(O,x(),"implementation")),d(t,v)});var Yr=i(Or,2);{var Wr=t=>{var s=Mt();d(t,s)};A(Yr,t=>{e(c),_(()=>Array.from(e(c).entries()).filter(([s,f])=>f.tab==="implementation").length===0)&&t(Wr)})}r(Ve),r(mr);var hr=i(mr,2),je=i(a(hr),2),Pr=a(je);xe(Pr,3,()=>(e(c),_(()=>Array.from(e(c).entries()).filter(([t,s])=>s.tab==="custom").sort((t,s)=>t[1].displayOrder-s[1].displayOrder))),([t,s])=>t,(t,s)=>{var f=wr(()=>kr(e(s),2));let x=()=>e(f)[0];var v=zt(),q=a(v);$r(q,{class:"w-3 h-3 mr-2 flex-shrink-0"});var W=i(q,2),X=a(W,!0);r(W),r(v),H(()=>{Ue(v,"aria-label",`${x()??""} field in Custom tab`),ie(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
|
|
5
|
+
${e(le)===x()&&e(M)!==x()?"border-t-2 border-purple-500":""}`),E(X,x())}),p("dragstart",v,O=>se(O,x())),p("dragend",v,z),p("dragover",v,O=>De(O,x())),p("dragleave",v,Me),p("drop",v,O=>ze(O,x(),"custom")),d(t,v)});var Xr=i(Pr,2);{var Kr=t=>{var s=At();d(t,s)};A(Xr,t=>{e(c),_(()=>Array.from(e(c).entries()).filter(([s,f])=>f.tab==="custom").length===0)&&t(Kr)})}r(je),r(hr);var Hr=i(hr,2),Le=i(a(Hr),2),Er=a(Le),Gr=a(Er);{var Qr=t=>{var s=Fe(),f=we(s);xe(f,1,()=>e(F),Ce,(x,v)=>{var q=Ft(),W=a(q),X=a(W,!0);r(W);var O=i(W,2);r(q),H(()=>E(X,e(v))),p("click",O,vt(()=>{o(F,e(F).filter(he=>he!==e(v)))})),d(x,q)}),d(t,s)},et=t=>{var s=Ot();H(()=>ie(s,1,`p-4 transition-colors
|
|
6
|
+
${e(K)==="mappings"?"bg-orange-50 dark:bg-orange-900/10":""}`)),p("dragover",s,f=>{f.preventDefault(),ge(f,"mappings")}),p("dragleave",s,pe),p("drop",s,f=>{if(f.preventDefault(),e(M)&&e(c).has(e(M))){e(F).includes(e(M))||o(F,[...e(F),e(M)]);const x=e(c).get(e(M));x.tab="mappings",e(c).set(e(M),x),o(c,new Map(e(c))),o(K,null)}}),d(t,s)};A(Gr,t=>{e(F),_(()=>e(F).length>0)?t(Qr):t(et,!1)})}r(Er),r(Le),r(Hr),r(zr),r(br);var Ir=i(br,2);{var rt=t=>{var s=It(),f=i(a(s),2),x=a(f),v=a(x),q=a(v);xe(q,5,()=>(e(m),_(()=>e(m).slice(0,5))),Ce,(X,O)=>{var he=Pt(),qe=a(he,!0);r(he),H(()=>E(qe,e(O))),d(X,he)}),r(q),r(v);var W=i(v);xe(W,5,()=>e(S),Ce,(X,O)=>{var he=Et();xe(he,5,()=>(e(m),_(()=>e(m).slice(0,5))),Ce,(qe,re)=>{var _e=Ht(),yr=a(_e,!0);r(_e),H(()=>E(yr,(e(O),e(re),_(()=>e(O)[e(re)]||"")))),d(qe,_e)}),r(he),d(X,he)}),r(W),r(x),r(f),r(s),d(t,s)};A(Ir,t=>{e(S),_(()=>e(S).length>0)&&t(rt)})}var Tr=i(Ir,2),Re=a(Tr),tt=a(Re);{var at=t=>{var s=Tt();d(t,s)},ot=t=>{var s=lr("Import to Control Set");d(t,s)};A(tt,t=>{e(Q)?t(at):t(ot,!1)})}r(Re),r(Tr),H(()=>{ie(or,1,`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 ${e(Z)?"":"border-red-500"}`),ie(Ie,1,`p-3 min-h-[400px] max-h-[600px] overflow-y-auto space-y-2 transition-colors
|
|
7
|
+
${e(K)===null?"bg-gray-100 dark:bg-gray-800":""}`),ie(Te,1,`p-3 min-h-[400px] max-h-[600px] overflow-y-auto space-y-2 transition-colors
|
|
8
|
+
${e(K)==="overview"?"bg-blue-50 dark:bg-blue-900/10":""}`),ie(Ve,1,`p-3 min-h-[400px] max-h-[600px] overflow-y-auto space-y-2 transition-colors
|
|
9
|
+
${e(K)==="implementation"?"bg-green-50 dark:bg-green-900/10":""}`),ie(je,1,`p-3 min-h-[400px] max-h-[600px] overflow-y-auto space-y-2 transition-colors
|
|
10
|
+
${e(K)==="custom"?"bg-purple-50 dark:bg-purple-900/10":""}`),ie(Le,1,`p-3 min-h-[400px] max-h-[600px] overflow-y-auto transition-colors
|
|
11
|
+
${e(K)==="mappings"?"bg-orange-50 dark:bg-orange-900/10":""}`),Re.disabled=e(Q)||!e(I)||!e(Z),Ue(Re,"title",e(Z)?"":"Please select a Control ID field")}),Lr(g,()=>e(B),t=>o(B,t)),Lr(R,()=>e(ae),t=>o(ae,t)),Cr(tr,()=>e(N),t=>o(N,t)),p("change",tr,()=>{ce()}),Cr(ar,()=>e(be),t=>o(be,t)),p("change",ar,ce),Cr(or,()=>e(Z),t=>o(Z,t)),p("dragover",Ie,t=>ge(t,null)),p("dragleave",Ie,pe),p("drop",Ie,t=>ye(t,null)),p("dragover",Te,t=>ge(t,"overview")),p("dragleave",Te,pe),p("drop",Te,t=>ye(t,"overview")),p("dragover",Ve,t=>ge(t,"implementation")),p("dragleave",Ve,pe),p("drop",Ve,t=>ye(t,"implementation")),p("dragover",je,t=>ge(t,"custom")),p("dragleave",je,pe),p("drop",je,t=>ye(t,"custom")),p("dragover",Le,t=>ge(t,"mappings")),p("dragleave",Le,pe),p("drop",Le,t=>ye(t,"mappings")),p("click",Re,nr),d(l,n)};A(cr,l=>{e(de),e(m),_(()=>e(de)&&e(m).length>0)&&l(G)})}r(He),H(()=>ie(Ee,1,`flex flex-col items-center justify-center w-full h-64 border-2 border-dashed rounded-lg cursor-pointer transition-all duration-300 ${e(ve)?"border-blue-500 bg-blue-50 dark:bg-blue-900/20":"border-gray-300 dark:border-gray-600 bg-gray-50 dark:bg-gray-700 hover:bg-gray-100 dark:hover:bg-gray-600"}`)),p("change",Ze,V),p("dragover",ke,b),p("dragleave",ke,$),p("drop",ke,j),d(me,He),Xe()}var Nt=h('<span class="ml-2"> </span>'),Bt=h('<div class="text-sm text-gray-500 dark:text-gray-400"><span class="font-medium"> </span> <!></div>'),Zt=h('<div class="text-center space-y-6"><div class="p-6 bg-gradient-to-br from-green-50 to-emerald-50 dark:from-green-900/20 dark:to-emerald-900/20 rounded-lg border-2 border-green-500"><svg class="w-12 h-12 text-green-600 mx-auto mb-4" fill="currentColor" viewBox="0 0 20 20" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M10 18a8 8 0 100-16 8 8 0 000 16zm3.707-9.293a1 1 0 00-1.414-1.414L9 10.586 7.707 9.293a1 1 0 00-1.414 1.414l2 2a1 1 0 001.414 0l4-4z" clip-rule="evenodd"></path></svg> <h3 class="text-lg font-semibold text-gray-900 dark:text-white mb-2">Already Using Control Set</h3> <p class="text-gray-600 dark:text-gray-400 mb-4">You are already using the only available control set in this project.</p> <!></div> <div class="flex justify-center gap-4"><a href="/" class="px-6 py-3 bg-blue-600 hover:bg-blue-700 text-white font-medium rounded-lg transition-colors shadow-lg">Continue to Controls</a> <button class="px-6 py-3 bg-gray-200 hover:bg-gray-300 dark:bg-gray-700 dark:hover:bg-gray-600 text-gray-900 dark:text-white font-medium rounded-lg transition-colors">Import New Control Set</button></div></div>'),Rt=h('<div class="p-4 text-sm text-yellow-800 rounded-lg bg-yellow-50 dark:bg-gray-800 dark:text-yellow-300"><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>'),qt=h('<p class="text-sm text-gray-600 dark:text-gray-400 mt-1"> </p>'),Ut=h('<span class="text-xs text-gray-500 dark:text-gray-400"><svg class="inline w-3 h-3 mr-1" fill="currentColor" viewBox="0 0 20 20"><path fill-rule="evenodd" d="M4 3a2 2 0 00-2 2v10a2 2 0 002 2h12a2 2 0 002-2V5a2 2 0 00-2-2H4zm12 12H4l4-8 3 6 2-4 3 6z" clip-rule="evenodd"></path></svg> </span>'),Jt=h('<div class="flex flex-col items-center"><svg class="w-8 h-8 text-green-600" fill="currentColor" viewBox="0 0 20 20" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M10 18a8 8 0 100-16 8 8 0 000 16zm3.707-9.293a1 1 0 00-1.414-1.414L9 10.586 7.707 9.293a1 1 0 00-1.414 1.414l2 2a1 1 0 001.414 0l4-4z" clip-rule="evenodd"></path></svg> <span class="text-xs text-green-600 font-medium mt-1">Current</span></div>'),Yt=Ne('<svg class="w-8 h-8 text-blue-600" fill="currentColor" viewBox="0 0 20 20" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M10 18a8 8 0 100-16 8 8 0 000 16zm3.707-9.293a1 1 0 00-1.414-1.414L9 10.586 7.707 9.293a1 1 0 00-1.414 1.414l2 2a1 1 0 001.414 0l4-4z" clip-rule="evenodd"></path></svg>'),Wt=Ne('<svg class="w-8 h-8 text-gray-300 dark:text-gray-600" fill="none" viewBox="0 0 20 20" xmlns="http://www.w3.org/2000/svg"><circle cx="10" cy="10" r="9" stroke="currentColor" stroke-width="2"></circle></svg>'),Xt=h('<div role="button" tabindex="0"><div class="flex items-center justify-between"><div class="flex-1"><div class="flex items-center gap-3"><h3 class="text-lg font-semibold text-gray-900 dark:text-white"> </h3> <span class="inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-medium bg-blue-100 text-blue-800 dark:bg-blue-900 dark:text-blue-200"> </span></div> <!> <div class="flex items-center gap-4 mt-2"><span class="text-xs text-gray-500 dark:text-gray-400"><svg class="inline w-3 h-3 mr-1" fill="currentColor" viewBox="0 0 20 20"><path fill-rule="evenodd" d="M2 6a2 2 0 012-2h12a2 2 0 012 2v8a2 2 0 01-2 2H4a2 2 0 01-2-2V6zm2-1a1 1 0 00-1 1v8a1 1 0 001 1h12a1 1 0 001-1V6a1 1 0 00-1-1H4z" clip-rule="evenodd"></path></svg> </span> <!></div></div> <div class="flex items-center ml-4"><!></div></div></div>'),Kt=h('<div class="mt-6 flex justify-center"><button class="px-6 py-3 bg-blue-600 hover:bg-blue-700 text-white font-medium rounded-lg transition-colors shadow-lg">Use Selected Control Set</button></div>'),Gt=h('<div class="space-y-3"></div> <!>',1),Qt=h('<div class="space-y-6"><div class="text-center"><h2 class="text-2xl font-bold text-gray-900 dark:text-white mb-2">Select an Existing Control Set</h2> <p class="text-gray-600 dark:text-gray-400">Choose from control sets found in your project directory</p></div> <!></div>');function ea(me,U){We(U,!1);const P=()=>it(Dr,"$appState",ne),[ne,I]=nt(),T=C(),N=Nr();let w=Je(U,"controlSets",24,()=>[]),m=C(""),S=C(null);function ue(B){F(B)||o(S,B)}function J(){e(S)&&!F(e(S))&&N("selected",{path:e(S).path})}function F(B){const ae=e(T)?.replace(/\/$/,""),Q=B.path?.replace(/\/$/,"");return!!(ae&&Q&&ae===Q)}Oe(()=>P(),()=>{o(T,P().currentPath||"")}),Oe(()=>te(w()),()=>{if(w())if(w().length===0)o(m,"No existing control sets found. Try importing from a spreadsheet instead.");else{const B=w().filter(ae=>!F(ae));B.length===1?o(S,B[0]):B.length===0&&w().length>0&&o(m,"already-using-only-set")}}),ir(),Ye();var c=Qt(),be=i(a(c),2);{var Z=B=>{var ae=Fe(),Q=we(ae);{var oe=de=>{var ve=Zt(),M=a(ve),K=i(a(M),6);{var le=$=>{var j=Bt(),V=a(j),Y=a(V,!0);r(V);var ce=i(V,2);{var Pe=$e=>{var se=Nt(),z=a(se);r(se),H(()=>E(z,`(${te(w()),_(()=>w()[0].controlCount)??""} controls)`)),d($e,se)};A(ce,$e=>{te(w()),_(()=>w()[0].controlCount)&&$e(Pe)})}r(j),H(()=>E(Y,(te(w()),_(()=>w()[0].name)))),d($,j)};A(K,$=>{te(w()),_(()=>w().length>0)&&$(le)})}r(M);var u=i(M,2),b=i(a(u),2);r(u),r(ve),p("click",b,()=>N("tab-change",{tab:"import"})),d(de,ve)},fe=de=>{var ve=Fe(),M=we(ve);{var K=u=>{var b=Rt(),$=a(b),j=i(a($),2),V=a(j,!0);r(j),r($),r(b),H(()=>E(V,e(m))),d(u,b)},le=u=>{var b=Fe(),$=we(b);{var j=V=>{var Y=Gt(),ce=we(Y);xe(ce,5,w,Ce,(se,z)=>{const ge=Ae(()=>(e(z),_(()=>F(e(z)))));var pe=Xt(),ye=a(pe),De=a(ye),Me=a(De),ze=a(Me),nr=a(ze,!0);r(ze);var He=i(ze,2),ke=a(He);r(He),r(Me);var Ee=i(Me,2);{var Be=G=>{var l=qt(),n=a(l,!0);r(l),H(()=>E(n,(e(z),_(()=>e(z).description)))),d(G,l)};A(Ee,G=>{e(z),_(()=>e(z).description)&&G(Be)})}var Ke=i(Ee,2),Ze=a(Ke),Ge=i(a(Ze));r(Ze);var dr=i(Ze,2);{var Qe=G=>{var l=Ut(),n=i(a(l));r(l),H(()=>E(n,` ${e(z),_(()=>e(z).file)??""}`)),d(G,l)};A(dr,G=>{e(z),_(()=>e(z).file)&&G(Qe)})}r(Ke),r(De);var er=i(De,2),rr=a(er);{var vr=G=>{var l=Jt();d(G,l)},cr=G=>{var l=Fe(),n=we(l);{var D=y=>{var g=Yt();d(y,g)},k=y=>{var g=Wt();d(y,g)};A(n,y=>{e(S)===e(z)?y(D):y(k,!1)},!0)}d(G,l)};A(rr,G=>{e(ge)?G(vr):G(cr,!1)})}r(er),r(ye),r(pe),H(()=>{ie(pe,1,`p-4 bg-gradient-to-br from-gray-50 to-gray-100 dark:from-gray-800 dark:to-gray-900 rounded-lg border-2 transition-all duration-200 ${e(ge)?"border-green-500 !bg-gradient-to-br from-green-50 to-emerald-50 dark:from-green-900/20 dark:to-emerald-900/20 cursor-not-allowed opacity-75":e(S)===e(z)?"border-blue-500 !bg-gradient-to-br from-blue-50 to-indigo-50 dark:from-blue-900/20 dark:to-indigo-900/20 shadow-lg cursor-pointer":"border-gray-200 dark:border-gray-700 hover:border-blue-300 dark:hover:border-blue-700 hover:shadow-md cursor-pointer"}`),E(nr,(e(z),_(()=>e(z).name))),E(ke,`${e(z),_(()=>e(z).controlCount||0)??""} controls`),E(Ge,` ${e(z),_(()=>e(z).path||"root")??""}`)}),p("click",pe,()=>!e(ge)&&ue(e(z))),p("keydown",pe,G=>G.key==="Enter"&&!e(ge)&&ue(e(z))),d(se,pe)}),r(ce);var Pe=i(ce,2);{var $e=se=>{var z=Kt(),ge=a(z);r(z),p("click",ge,J),d(se,z)};A(Pe,se=>{e(S)&&se($e)})}d(V,Y)};A($,V=>{te(w()),_(()=>w().length>0)&&V(j)},!0)}d(u,b)};A(M,u=>{e(m)?u(K):u(le,!1)},!0)}d(de,ve)};A(Q,de=>{e(m)==="already-using-only-set"?de(oe):de(fe,!1)},!0)}d(B,ae)};A(be,B=>{B(Z,!1)})}r(c),d(me,c),Xe(),I()}var ra=h('<div class="flex justify-center mb-8"><div class="bg-gradient-to-br from-gray-50 to-gray-100 dark:from-gray-800 dark:to-gray-900 rounded-lg shadow-sm border border-gray-200 dark:border-gray-700 inline-flex"><button data-testid="tab-existing">Select Existing Control Set</button> <button data-testid="tab-import">Import New from Spreadsheet</button></div></div>'),ta=h('<div data-testid="pane-import"><!></div>'),aa=h('<div data-testid="pane-existing"><!></div>'),oa=h('<div data-testid="switch-overlay" class="absolute inset-0 bg-gray-900 bg-opacity-50 flex items-center justify-center rounded-lg"><div class="bg-white dark:bg-gray-700 rounded-lg p-6 text-center"><svg class="animate-spin h-8 w-8 text-blue-600 mx-auto mb-4" 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> <p class="text-gray-900 dark:text-white">Switching control set...</p></div></div>'),la=h('<div class=" p-4"><div class="max-w-6xl mx-auto"><div class="text-center py-8"><h1 class="text-4xl font-extrabold text-gray-900 dark:text-white mb-4 flex items-center justify-center gap-3"><img src="/lula.png" class="h-12 w-12" alt="Lula"/> <span id="title">Lula</span></h1> <p id="description" class="text-lg text-gray-600 dark:text-gray-400"><!></p></div> <!> <div class="bg-gradient-to-br from-gray-50 to-gray-100 dark:from-gray-800 dark:to-gray-900 rounded-lg shadow-xl p-6 relative border border-gray-200 dark:border-gray-700"><!> <!></div></div></div>');function ga(me,U){We(U,!1);let P=C("import"),ne=C(""),I=C(!1),T=C(!1),N=C([]);lt(()=>{Sr.connect();const u=Dr.subscribe($=>{$.name&&$.name!=="Unknown Control Set"&&$.id!=="unknown"&&$.id!=="default"&&o(ne,$.currentPath||"")}),b=async $=>{const j=$.detail;if(j&&j.controlSets){if(o(N,j.controlSets||[]),o(I,e(N).length>0),e(N).length===1){const V=e(N)[0];if(!(e(ne)&&e(ne).includes(V.path))&&!e(ne)){console.log("Only one control set found and none loaded, auto-loading:",V.path),await w(V.path);return}}e(I)&&o(P,"existing")}};return window.addEventListener("control-sets-list",b),Sr.scanControlSets().catch($=>{console.error("Error scanning control sets:",$)}),()=>{u(),window.removeEventListener("control-sets-list",b)}});async function w(u){console.log("Starting control set switch to:",u),o(T,!0);try{await Sr.switchControlSet(u),console.log("WebSocket command sent, waiting for state update..."),await new Promise(b=>{let $=0;const j=50,V=setInterval(()=>{$++;const Y=st(Dr);!Y.isSwitchingControlSet&&Y.currentPath&&Y.currentPath.includes(u)?(clearInterval(V),console.log("Control set switch completed successfully"),b()):$>=j&&(clearInterval(V),console.error("Control set switch timed out"),alert("Control set switch timed out. Please try again."),b())},100)}),o(T,!1),dt("/")}catch(b){console.error("Error switching control set:",b),alert("Failed to switch control set: "+b.message),o(T,!1)}}async function m(u){const{path:b}=u.detail;await w(b)}async function S(u){const{path:b}=u.detail;await w(b)}function ue(u){const{tab:b}=u.detail;b&&o(P,b)}Ye();var J=la(),F=a(J),c=a(F),be=i(a(c),2),Z=a(be);{var B=u=>{var b=lr("You have an existing control set. You can continue using it or create a new one.");d(u,b)},ae=u=>{var b=Fe(),$=we(b);{var j=Y=>{var ce=lr("Select an existing control set or import a new one from a spreadsheet.");d(Y,ce)},V=Y=>{var ce=lr("Let's get started by importing a control set from a spreadsheet.");d(Y,ce)};A($,Y=>{e(I)?Y(j):Y(V,!1)},!0)}d(u,b)};A(Z,u=>{e(ne)?u(B):u(ae,!1)})}r(be),r(c);var Q=i(c,2);{var oe=u=>{var b=ra(),$=a(b),j=a($),V=i(j,2);r($),r(b),H(()=>{ie(j,1,`px-6 py-3 rounded-l-lg font-medium transition-colors ${e(P)==="existing"?"bg-blue-600 text-white":"text-gray-600 dark:text-gray-400 hover:text-gray-900 dark:hover:text-white"}`),ie(V,1,`px-6 py-3 rounded-r-lg font-medium transition-colors ${e(P)==="import"?"bg-blue-600 text-white":"text-gray-600 dark:text-gray-400 hover:text-gray-900 dark:hover:text-white"}`)}),p("click",j,()=>o(P,"existing")),p("click",V,()=>o(P,"import")),d(u,b)};A(Q,u=>{e(I)&&u(oe)})}var fe=i(Q,2),de=a(fe);{var ve=u=>{var b=ta(),$=a(b);Lt($,{$$events:{created:m}}),r(b),d(u,b)},M=u=>{var b=aa(),$=a(b);ea($,{get controlSets(){return e(N)},$$events:{selected:S,"tab-change":ue}}),r(b),d(u,b)};A(de,u=>{e(P)==="import"?u(ve):u(M,!1)})}var K=i(de,2);{var le=u=>{var b=oa();d(u,b)};A(K,u=>{e(T)&&u(le)})}r(fe),r(F),r(J),d(me,J),Xe()}export{ga as component};
|
package/dist/_app/version.json
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":"
|
|
1
|
+
{"version":"1758206542286"}
|
package/dist/cli/commands/ui.js
CHANGED
|
@@ -1703,7 +1703,9 @@ var init_fileStore = __esm({
|
|
|
1703
1703
|
* Get simple filename from control ID
|
|
1704
1704
|
*/
|
|
1705
1705
|
getControlFilename(controlId) {
|
|
1706
|
-
const sanitized = controlId.replace(
|
|
1706
|
+
const sanitized = controlId.replace(/^([A-Z]+)-(.*)/, (match, prefix, suffix) => {
|
|
1707
|
+
return `${prefix}-${suffix.replace(/[^\w]/g, "_")}`;
|
|
1708
|
+
});
|
|
1707
1709
|
return `${sanitized}.yaml`;
|
|
1708
1710
|
}
|
|
1709
1711
|
/**
|
|
@@ -3293,7 +3295,7 @@ var init_spreadsheetRoutes = __esm({
|
|
|
3293
3295
|
if (req.body.fieldSchema) {
|
|
3294
3296
|
try {
|
|
3295
3297
|
frontendFieldSchema = JSON.parse(req.body.fieldSchema);
|
|
3296
|
-
} catch
|
|
3298
|
+
} catch {
|
|
3297
3299
|
}
|
|
3298
3300
|
}
|
|
3299
3301
|
const fields = {};
|
|
@@ -3353,9 +3355,7 @@ var init_spreadsheetRoutes = __esm({
|
|
|
3353
3355
|
uiType = "long_text";
|
|
3354
3356
|
}
|
|
3355
3357
|
let category = frontendConfig?.category || "custom";
|
|
3356
|
-
if (
|
|
3357
|
-
category = "mappings";
|
|
3358
|
-
} else if (!frontendConfig) {
|
|
3358
|
+
if (!frontendConfig) {
|
|
3359
3359
|
if (fieldName.includes("status") || fieldName.includes("state")) {
|
|
3360
3360
|
category = "compliance";
|
|
3361
3361
|
} else if (fieldName.includes("title") || fieldName.includes("name") || fieldName.includes("description")) {
|
|
@@ -3383,7 +3383,7 @@ var init_spreadsheetRoutes = __esm({
|
|
|
3383
3383
|
// Control ID is always first
|
|
3384
3384
|
category: isControlIdField ? "core" : category,
|
|
3385
3385
|
// Control ID is always core
|
|
3386
|
-
tab: isControlIdField ? "overview" :
|
|
3386
|
+
tab: isControlIdField ? "overview" : frontendConfig?.tab || void 0
|
|
3387
3387
|
// Use frontend config or default
|
|
3388
3388
|
};
|
|
3389
3389
|
if (uiType === "select") {
|
|
@@ -3412,7 +3412,6 @@ var init_spreadsheetRoutes = __esm({
|
|
|
3412
3412
|
writeFileSync2(join4(baseDir, "lula.yaml"), yaml4.dump(controlSetData));
|
|
3413
3413
|
const controlsDir = join4(baseDir, "controls");
|
|
3414
3414
|
const mappingsDir = join4(baseDir, "mappings");
|
|
3415
|
-
const justificationFieldNames = justificationFields;
|
|
3416
3415
|
families.forEach((familyControls, family) => {
|
|
3417
3416
|
const familyDir = join4(controlsDir, family);
|
|
3418
3417
|
const familyMappingsDir = join4(mappingsDir, family);
|
|
@@ -3447,7 +3446,6 @@ var init_spreadsheetRoutes = __esm({
|
|
|
3447
3446
|
if (fieldName === "family") return;
|
|
3448
3447
|
if (justificationFields.includes(fieldName) && control[fieldName] !== void 0 && control[fieldName] !== null) {
|
|
3449
3448
|
justificationContents.push(control[fieldName]);
|
|
3450
|
-
filteredControl[fieldName] = control[fieldName];
|
|
3451
3449
|
}
|
|
3452
3450
|
const isInFrontendSchema = frontendFieldSchema?.some((f) => f.fieldName === fieldName);
|
|
3453
3451
|
const isInFieldsMetadata = fields.hasOwnProperty(fieldName);
|
|
@@ -3563,7 +3561,7 @@ var init_spreadsheetRoutes = __esm({
|
|
|
3563
3561
|
}
|
|
3564
3562
|
const headerCandidates = rows.slice(0, 5).map((row, index) => ({
|
|
3565
3563
|
row: index + 1,
|
|
3566
|
-
preview: row.slice(0, 4).filter((v) => v
|
|
3564
|
+
preview: row.slice(0, 4).filter((v) => v !== null).filter((v) => v !== void 0).join(", ") + (row.length > 4 ? ", ..." : "")
|
|
3567
3565
|
}));
|
|
3568
3566
|
res.json({
|
|
3569
3567
|
sheets,
|
package/dist/cli/server/index.js
CHANGED
|
@@ -1685,7 +1685,9 @@ var init_fileStore = __esm({
|
|
|
1685
1685
|
* Get simple filename from control ID
|
|
1686
1686
|
*/
|
|
1687
1687
|
getControlFilename(controlId) {
|
|
1688
|
-
const sanitized = controlId.replace(
|
|
1688
|
+
const sanitized = controlId.replace(/^([A-Z]+)-(.*)/, (match, prefix, suffix) => {
|
|
1689
|
+
return `${prefix}-${suffix.replace(/[^\w]/g, "_")}`;
|
|
1690
|
+
});
|
|
1689
1691
|
return `${sanitized}.yaml`;
|
|
1690
1692
|
}
|
|
1691
1693
|
/**
|
|
@@ -3275,7 +3277,7 @@ var init_spreadsheetRoutes = __esm({
|
|
|
3275
3277
|
if (req.body.fieldSchema) {
|
|
3276
3278
|
try {
|
|
3277
3279
|
frontendFieldSchema = JSON.parse(req.body.fieldSchema);
|
|
3278
|
-
} catch
|
|
3280
|
+
} catch {
|
|
3279
3281
|
}
|
|
3280
3282
|
}
|
|
3281
3283
|
const fields = {};
|
|
@@ -3335,9 +3337,7 @@ var init_spreadsheetRoutes = __esm({
|
|
|
3335
3337
|
uiType = "long_text";
|
|
3336
3338
|
}
|
|
3337
3339
|
let category = frontendConfig?.category || "custom";
|
|
3338
|
-
if (
|
|
3339
|
-
category = "mappings";
|
|
3340
|
-
} else if (!frontendConfig) {
|
|
3340
|
+
if (!frontendConfig) {
|
|
3341
3341
|
if (fieldName.includes("status") || fieldName.includes("state")) {
|
|
3342
3342
|
category = "compliance";
|
|
3343
3343
|
} else if (fieldName.includes("title") || fieldName.includes("name") || fieldName.includes("description")) {
|
|
@@ -3365,7 +3365,7 @@ var init_spreadsheetRoutes = __esm({
|
|
|
3365
3365
|
// Control ID is always first
|
|
3366
3366
|
category: isControlIdField ? "core" : category,
|
|
3367
3367
|
// Control ID is always core
|
|
3368
|
-
tab: isControlIdField ? "overview" :
|
|
3368
|
+
tab: isControlIdField ? "overview" : frontendConfig?.tab || void 0
|
|
3369
3369
|
// Use frontend config or default
|
|
3370
3370
|
};
|
|
3371
3371
|
if (uiType === "select") {
|
|
@@ -3394,7 +3394,6 @@ var init_spreadsheetRoutes = __esm({
|
|
|
3394
3394
|
writeFileSync2(join4(baseDir, "lula.yaml"), yaml4.dump(controlSetData));
|
|
3395
3395
|
const controlsDir = join4(baseDir, "controls");
|
|
3396
3396
|
const mappingsDir = join4(baseDir, "mappings");
|
|
3397
|
-
const justificationFieldNames = justificationFields;
|
|
3398
3397
|
families.forEach((familyControls, family) => {
|
|
3399
3398
|
const familyDir = join4(controlsDir, family);
|
|
3400
3399
|
const familyMappingsDir = join4(mappingsDir, family);
|
|
@@ -3429,7 +3428,6 @@ var init_spreadsheetRoutes = __esm({
|
|
|
3429
3428
|
if (fieldName === "family") return;
|
|
3430
3429
|
if (justificationFields.includes(fieldName) && control[fieldName] !== void 0 && control[fieldName] !== null) {
|
|
3431
3430
|
justificationContents.push(control[fieldName]);
|
|
3432
|
-
filteredControl[fieldName] = control[fieldName];
|
|
3433
3431
|
}
|
|
3434
3432
|
const isInFrontendSchema = frontendFieldSchema?.some((f) => f.fieldName === fieldName);
|
|
3435
3433
|
const isInFieldsMetadata = fields.hasOwnProperty(fieldName);
|
|
@@ -3545,7 +3543,7 @@ var init_spreadsheetRoutes = __esm({
|
|
|
3545
3543
|
}
|
|
3546
3544
|
const headerCandidates = rows.slice(0, 5).map((row, index) => ({
|
|
3547
3545
|
row: index + 1,
|
|
3548
|
-
preview: row.slice(0, 4).filter((v) => v
|
|
3546
|
+
preview: row.slice(0, 4).filter((v) => v !== null).filter((v) => v !== void 0).join(", ") + (row.length > 4 ? ", ..." : "")
|
|
3549
3547
|
}));
|
|
3550
3548
|
res.json({
|
|
3551
3549
|
sheets,
|
|
@@ -1685,7 +1685,9 @@ var init_fileStore = __esm({
|
|
|
1685
1685
|
* Get simple filename from control ID
|
|
1686
1686
|
*/
|
|
1687
1687
|
getControlFilename(controlId) {
|
|
1688
|
-
const sanitized = controlId.replace(
|
|
1688
|
+
const sanitized = controlId.replace(/^([A-Z]+)-(.*)/, (match, prefix, suffix) => {
|
|
1689
|
+
return `${prefix}-${suffix.replace(/[^\w]/g, "_")}`;
|
|
1690
|
+
});
|
|
1689
1691
|
return `${sanitized}.yaml`;
|
|
1690
1692
|
}
|
|
1691
1693
|
/**
|
|
@@ -3275,7 +3277,7 @@ var init_spreadsheetRoutes = __esm({
|
|
|
3275
3277
|
if (req.body.fieldSchema) {
|
|
3276
3278
|
try {
|
|
3277
3279
|
frontendFieldSchema = JSON.parse(req.body.fieldSchema);
|
|
3278
|
-
} catch
|
|
3280
|
+
} catch {
|
|
3279
3281
|
}
|
|
3280
3282
|
}
|
|
3281
3283
|
const fields = {};
|
|
@@ -3335,9 +3337,7 @@ var init_spreadsheetRoutes = __esm({
|
|
|
3335
3337
|
uiType = "long_text";
|
|
3336
3338
|
}
|
|
3337
3339
|
let category = frontendConfig?.category || "custom";
|
|
3338
|
-
if (
|
|
3339
|
-
category = "mappings";
|
|
3340
|
-
} else if (!frontendConfig) {
|
|
3340
|
+
if (!frontendConfig) {
|
|
3341
3341
|
if (fieldName.includes("status") || fieldName.includes("state")) {
|
|
3342
3342
|
category = "compliance";
|
|
3343
3343
|
} else if (fieldName.includes("title") || fieldName.includes("name") || fieldName.includes("description")) {
|
|
@@ -3365,7 +3365,7 @@ var init_spreadsheetRoutes = __esm({
|
|
|
3365
3365
|
// Control ID is always first
|
|
3366
3366
|
category: isControlIdField ? "core" : category,
|
|
3367
3367
|
// Control ID is always core
|
|
3368
|
-
tab: isControlIdField ? "overview" :
|
|
3368
|
+
tab: isControlIdField ? "overview" : frontendConfig?.tab || void 0
|
|
3369
3369
|
// Use frontend config or default
|
|
3370
3370
|
};
|
|
3371
3371
|
if (uiType === "select") {
|
|
@@ -3394,7 +3394,6 @@ var init_spreadsheetRoutes = __esm({
|
|
|
3394
3394
|
writeFileSync2(join4(baseDir, "lula.yaml"), yaml4.dump(controlSetData));
|
|
3395
3395
|
const controlsDir = join4(baseDir, "controls");
|
|
3396
3396
|
const mappingsDir = join4(baseDir, "mappings");
|
|
3397
|
-
const justificationFieldNames = justificationFields;
|
|
3398
3397
|
families.forEach((familyControls, family) => {
|
|
3399
3398
|
const familyDir = join4(controlsDir, family);
|
|
3400
3399
|
const familyMappingsDir = join4(mappingsDir, family);
|
|
@@ -3429,7 +3428,6 @@ var init_spreadsheetRoutes = __esm({
|
|
|
3429
3428
|
if (fieldName === "family") return;
|
|
3430
3429
|
if (justificationFields.includes(fieldName) && control[fieldName] !== void 0 && control[fieldName] !== null) {
|
|
3431
3430
|
justificationContents.push(control[fieldName]);
|
|
3432
|
-
filteredControl[fieldName] = control[fieldName];
|
|
3433
3431
|
}
|
|
3434
3432
|
const isInFrontendSchema = frontendFieldSchema?.some((f) => f.fieldName === fieldName);
|
|
3435
3433
|
const isInFieldsMetadata = fields.hasOwnProperty(fieldName);
|
|
@@ -3545,7 +3543,7 @@ var init_spreadsheetRoutes = __esm({
|
|
|
3545
3543
|
}
|
|
3546
3544
|
const headerCandidates = rows.slice(0, 5).map((row, index) => ({
|
|
3547
3545
|
row: index + 1,
|
|
3548
|
-
preview: row.slice(0, 4).filter((v) => v
|
|
3546
|
+
preview: row.slice(0, 4).filter((v) => v !== null).filter((v) => v !== void 0).join(", ") + (row.length > 4 ? ", ..." : "")
|
|
3549
3547
|
}));
|
|
3550
3548
|
res.json({
|
|
3551
3549
|
sheets,
|
|
@@ -106,7 +106,9 @@ var FileStore = class {
|
|
|
106
106
|
* Get simple filename from control ID
|
|
107
107
|
*/
|
|
108
108
|
getControlFilename(controlId) {
|
|
109
|
-
const sanitized = controlId.replace(
|
|
109
|
+
const sanitized = controlId.replace(/^([A-Z]+)-(.*)/, (match, prefix, suffix) => {
|
|
110
|
+
return `${prefix}-${suffix.replace(/[^\w]/g, "_")}`;
|
|
111
|
+
});
|
|
110
112
|
return `${sanitized}.yaml`;
|
|
111
113
|
}
|
|
112
114
|
/**
|
|
@@ -234,7 +234,7 @@ router.post("/import-spreadsheet", upload.single("file"), async (req, res) => {
|
|
|
234
234
|
if (req.body.fieldSchema) {
|
|
235
235
|
try {
|
|
236
236
|
frontendFieldSchema = JSON.parse(req.body.fieldSchema);
|
|
237
|
-
} catch
|
|
237
|
+
} catch {
|
|
238
238
|
}
|
|
239
239
|
}
|
|
240
240
|
const fields = {};
|
|
@@ -294,9 +294,7 @@ router.post("/import-spreadsheet", upload.single("file"), async (req, res) => {
|
|
|
294
294
|
uiType = "long_text";
|
|
295
295
|
}
|
|
296
296
|
let category = frontendConfig?.category || "custom";
|
|
297
|
-
if (
|
|
298
|
-
category = "mappings";
|
|
299
|
-
} else if (!frontendConfig) {
|
|
297
|
+
if (!frontendConfig) {
|
|
300
298
|
if (fieldName.includes("status") || fieldName.includes("state")) {
|
|
301
299
|
category = "compliance";
|
|
302
300
|
} else if (fieldName.includes("title") || fieldName.includes("name") || fieldName.includes("description")) {
|
|
@@ -324,7 +322,7 @@ router.post("/import-spreadsheet", upload.single("file"), async (req, res) => {
|
|
|
324
322
|
// Control ID is always first
|
|
325
323
|
category: isControlIdField ? "core" : category,
|
|
326
324
|
// Control ID is always core
|
|
327
|
-
tab: isControlIdField ? "overview" :
|
|
325
|
+
tab: isControlIdField ? "overview" : frontendConfig?.tab || void 0
|
|
328
326
|
// Use frontend config or default
|
|
329
327
|
};
|
|
330
328
|
if (uiType === "select") {
|
|
@@ -353,7 +351,6 @@ router.post("/import-spreadsheet", upload.single("file"), async (req, res) => {
|
|
|
353
351
|
writeFileSync(join(baseDir, "lula.yaml"), yaml4.dump(controlSetData));
|
|
354
352
|
const controlsDir = join(baseDir, "controls");
|
|
355
353
|
const mappingsDir = join(baseDir, "mappings");
|
|
356
|
-
const justificationFieldNames = justificationFields;
|
|
357
354
|
families.forEach((familyControls, family) => {
|
|
358
355
|
const familyDir = join(controlsDir, family);
|
|
359
356
|
const familyMappingsDir = join(mappingsDir, family);
|
|
@@ -388,7 +385,6 @@ router.post("/import-spreadsheet", upload.single("file"), async (req, res) => {
|
|
|
388
385
|
if (fieldName === "family") return;
|
|
389
386
|
if (justificationFields.includes(fieldName) && control[fieldName] !== void 0 && control[fieldName] !== null) {
|
|
390
387
|
justificationContents.push(control[fieldName]);
|
|
391
|
-
filteredControl[fieldName] = control[fieldName];
|
|
392
388
|
}
|
|
393
389
|
const isInFrontendSchema = frontendFieldSchema?.some((f) => f.fieldName === fieldName);
|
|
394
390
|
const isInFieldsMetadata = fields.hasOwnProperty(fieldName);
|
|
@@ -753,7 +749,7 @@ router.post("/parse-excel", upload.single("file"), async (req, res) => {
|
|
|
753
749
|
}
|
|
754
750
|
const headerCandidates = rows.slice(0, 5).map((row, index) => ({
|
|
755
751
|
row: index + 1,
|
|
756
|
-
preview: row.slice(0, 4).filter((v) => v
|
|
752
|
+
preview: row.slice(0, 4).filter((v) => v !== null).filter((v) => v !== void 0).join(", ") + (row.length > 4 ? ", ..." : "")
|
|
757
753
|
}));
|
|
758
754
|
res.json({
|
|
759
755
|
sheets,
|
|
@@ -132,7 +132,9 @@ var init_fileStore = __esm({
|
|
|
132
132
|
* Get simple filename from control ID
|
|
133
133
|
*/
|
|
134
134
|
getControlFilename(controlId) {
|
|
135
|
-
const sanitized = controlId.replace(
|
|
135
|
+
const sanitized = controlId.replace(/^([A-Z]+)-(.*)/, (match, prefix, suffix) => {
|
|
136
|
+
return `${prefix}-${suffix.replace(/[^\w]/g, "_")}`;
|
|
137
|
+
});
|
|
136
138
|
return `${sanitized}.yaml`;
|
|
137
139
|
}
|
|
138
140
|
/**
|
|
@@ -1722,7 +1724,7 @@ var init_spreadsheetRoutes = __esm({
|
|
|
1722
1724
|
if (req.body.fieldSchema) {
|
|
1723
1725
|
try {
|
|
1724
1726
|
frontendFieldSchema = JSON.parse(req.body.fieldSchema);
|
|
1725
|
-
} catch
|
|
1727
|
+
} catch {
|
|
1726
1728
|
}
|
|
1727
1729
|
}
|
|
1728
1730
|
const fields = {};
|
|
@@ -1782,9 +1784,7 @@ var init_spreadsheetRoutes = __esm({
|
|
|
1782
1784
|
uiType = "long_text";
|
|
1783
1785
|
}
|
|
1784
1786
|
let category = frontendConfig?.category || "custom";
|
|
1785
|
-
if (
|
|
1786
|
-
category = "mappings";
|
|
1787
|
-
} else if (!frontendConfig) {
|
|
1787
|
+
if (!frontendConfig) {
|
|
1788
1788
|
if (fieldName.includes("status") || fieldName.includes("state")) {
|
|
1789
1789
|
category = "compliance";
|
|
1790
1790
|
} else if (fieldName.includes("title") || fieldName.includes("name") || fieldName.includes("description")) {
|
|
@@ -1812,7 +1812,7 @@ var init_spreadsheetRoutes = __esm({
|
|
|
1812
1812
|
// Control ID is always first
|
|
1813
1813
|
category: isControlIdField ? "core" : category,
|
|
1814
1814
|
// Control ID is always core
|
|
1815
|
-
tab: isControlIdField ? "overview" :
|
|
1815
|
+
tab: isControlIdField ? "overview" : frontendConfig?.tab || void 0
|
|
1816
1816
|
// Use frontend config or default
|
|
1817
1817
|
};
|
|
1818
1818
|
if (uiType === "select") {
|
|
@@ -1841,7 +1841,6 @@ var init_spreadsheetRoutes = __esm({
|
|
|
1841
1841
|
writeFileSync2(join4(baseDir, "lula.yaml"), yaml4.dump(controlSetData));
|
|
1842
1842
|
const controlsDir = join4(baseDir, "controls");
|
|
1843
1843
|
const mappingsDir = join4(baseDir, "mappings");
|
|
1844
|
-
const justificationFieldNames = justificationFields;
|
|
1845
1844
|
families.forEach((familyControls, family) => {
|
|
1846
1845
|
const familyDir = join4(controlsDir, family);
|
|
1847
1846
|
const familyMappingsDir = join4(mappingsDir, family);
|
|
@@ -1876,7 +1875,6 @@ var init_spreadsheetRoutes = __esm({
|
|
|
1876
1875
|
if (fieldName === "family") return;
|
|
1877
1876
|
if (justificationFields.includes(fieldName) && control[fieldName] !== void 0 && control[fieldName] !== null) {
|
|
1878
1877
|
justificationContents.push(control[fieldName]);
|
|
1879
|
-
filteredControl[fieldName] = control[fieldName];
|
|
1880
1878
|
}
|
|
1881
1879
|
const isInFrontendSchema = frontendFieldSchema?.some((f) => f.fieldName === fieldName);
|
|
1882
1880
|
const isInFieldsMetadata = fields.hasOwnProperty(fieldName);
|
|
@@ -1992,7 +1990,7 @@ var init_spreadsheetRoutes = __esm({
|
|
|
1992
1990
|
}
|
|
1993
1991
|
const headerCandidates = rows.slice(0, 5).map((row, index) => ({
|
|
1994
1992
|
row: index + 1,
|
|
1995
|
-
preview: row.slice(0, 4).filter((v) => v
|
|
1993
|
+
preview: row.slice(0, 4).filter((v) => v !== null).filter((v) => v !== void 0).join(", ") + (row.length > 4 ? ", ..." : "")
|
|
1996
1994
|
}));
|
|
1997
1995
|
res.json({
|
|
1998
1996
|
sheets,
|
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.CuPBZjFh.js">
|
|
10
|
+
<link rel="modulepreload" href="/_app/immutable/chunks/z5X83jua.js">
|
|
11
|
+
<link rel="modulepreload" href="/_app/immutable/chunks/C9p1zaS8.js">
|
|
12
|
+
<link rel="modulepreload" href="/_app/immutable/entry/app.DlQKiOqI.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/C2RwV308.js">
|
|
15
|
+
<link rel="modulepreload" href="/_app/immutable/chunks/EhHGBAhB.js">
|
|
16
|
+
<link rel="modulepreload" href="/_app/immutable/chunks/cvagBx-i.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_ni8atx = {
|
|
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.CuPBZjFh.js"),
|
|
30
|
+
import("/_app/immutable/entry/app.DlQKiOqI.js")
|
|
31
31
|
]).then(([kit, app]) => {
|
|
32
32
|
kit.start(app, element);
|
|
33
33
|
});
|
package/dist/index.js
CHANGED
|
@@ -1704,7 +1704,9 @@ var init_fileStore = __esm({
|
|
|
1704
1704
|
* Get simple filename from control ID
|
|
1705
1705
|
*/
|
|
1706
1706
|
getControlFilename(controlId) {
|
|
1707
|
-
const sanitized = controlId.replace(
|
|
1707
|
+
const sanitized = controlId.replace(/^([A-Z]+)-(.*)/, (match, prefix, suffix) => {
|
|
1708
|
+
return `${prefix}-${suffix.replace(/[^\w]/g, "_")}`;
|
|
1709
|
+
});
|
|
1708
1710
|
return `${sanitized}.yaml`;
|
|
1709
1711
|
}
|
|
1710
1712
|
/**
|
|
@@ -3294,7 +3296,7 @@ var init_spreadsheetRoutes = __esm({
|
|
|
3294
3296
|
if (req.body.fieldSchema) {
|
|
3295
3297
|
try {
|
|
3296
3298
|
frontendFieldSchema = JSON.parse(req.body.fieldSchema);
|
|
3297
|
-
} catch
|
|
3299
|
+
} catch {
|
|
3298
3300
|
}
|
|
3299
3301
|
}
|
|
3300
3302
|
const fields = {};
|
|
@@ -3354,9 +3356,7 @@ var init_spreadsheetRoutes = __esm({
|
|
|
3354
3356
|
uiType = "long_text";
|
|
3355
3357
|
}
|
|
3356
3358
|
let category = frontendConfig?.category || "custom";
|
|
3357
|
-
if (
|
|
3358
|
-
category = "mappings";
|
|
3359
|
-
} else if (!frontendConfig) {
|
|
3359
|
+
if (!frontendConfig) {
|
|
3360
3360
|
if (fieldName.includes("status") || fieldName.includes("state")) {
|
|
3361
3361
|
category = "compliance";
|
|
3362
3362
|
} else if (fieldName.includes("title") || fieldName.includes("name") || fieldName.includes("description")) {
|
|
@@ -3384,7 +3384,7 @@ var init_spreadsheetRoutes = __esm({
|
|
|
3384
3384
|
// Control ID is always first
|
|
3385
3385
|
category: isControlIdField ? "core" : category,
|
|
3386
3386
|
// Control ID is always core
|
|
3387
|
-
tab: isControlIdField ? "overview" :
|
|
3387
|
+
tab: isControlIdField ? "overview" : frontendConfig?.tab || void 0
|
|
3388
3388
|
// Use frontend config or default
|
|
3389
3389
|
};
|
|
3390
3390
|
if (uiType === "select") {
|
|
@@ -3413,7 +3413,6 @@ var init_spreadsheetRoutes = __esm({
|
|
|
3413
3413
|
writeFileSync2(join4(baseDir, "lula.yaml"), yaml4.dump(controlSetData));
|
|
3414
3414
|
const controlsDir = join4(baseDir, "controls");
|
|
3415
3415
|
const mappingsDir = join4(baseDir, "mappings");
|
|
3416
|
-
const justificationFieldNames = justificationFields;
|
|
3417
3416
|
families.forEach((familyControls, family) => {
|
|
3418
3417
|
const familyDir = join4(controlsDir, family);
|
|
3419
3418
|
const familyMappingsDir = join4(mappingsDir, family);
|
|
@@ -3448,7 +3447,6 @@ var init_spreadsheetRoutes = __esm({
|
|
|
3448
3447
|
if (fieldName === "family") return;
|
|
3449
3448
|
if (justificationFields.includes(fieldName) && control[fieldName] !== void 0 && control[fieldName] !== null) {
|
|
3450
3449
|
justificationContents.push(control[fieldName]);
|
|
3451
|
-
filteredControl[fieldName] = control[fieldName];
|
|
3452
3450
|
}
|
|
3453
3451
|
const isInFrontendSchema = frontendFieldSchema?.some((f) => f.fieldName === fieldName);
|
|
3454
3452
|
const isInFieldsMetadata = fields.hasOwnProperty(fieldName);
|
|
@@ -3564,7 +3562,7 @@ var init_spreadsheetRoutes = __esm({
|
|
|
3564
3562
|
}
|
|
3565
3563
|
const headerCandidates = rows.slice(0, 5).map((row, index) => ({
|
|
3566
3564
|
row: index + 1,
|
|
3567
|
-
preview: row.slice(0, 4).filter((v) => v
|
|
3565
|
+
preview: row.slice(0, 4).filter((v) => v !== null).filter((v) => v !== void 0).join(", ") + (row.length > 4 ? ", ..." : "")
|
|
3568
3566
|
}));
|
|
3569
3567
|
res.json({
|
|
3570
3568
|
sheets,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "lula2",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.3",
|
|
4
4
|
"description": "A tool for managing compliance as code in your GitHub repositories.",
|
|
5
5
|
"bin": {
|
|
6
6
|
"lula2": "./dist/lula2"
|
|
@@ -98,7 +98,7 @@
|
|
|
98
98
|
"esbuild": "^0.25.9",
|
|
99
99
|
"eslint": "^9.35.0",
|
|
100
100
|
"eslint-config-prettier": "^10.1.8",
|
|
101
|
-
"eslint-plugin-jsdoc": "^
|
|
101
|
+
"eslint-plugin-jsdoc": "^58.1.1",
|
|
102
102
|
"eslint-plugin-svelte": "^3.12.2",
|
|
103
103
|
"globals": "^16.3.0",
|
|
104
104
|
"husky": "^9.1.7",
|
|
@@ -23,7 +23,7 @@
|
|
|
23
23
|
<div class="overflow-x-auto">
|
|
24
24
|
<table class="min-w-full divide-y divide-gray-200 dark:divide-gray-700">
|
|
25
25
|
<tbody class="divide-y divide-gray-200 dark:divide-gray-700">
|
|
26
|
-
{#each section.data.rows as row,
|
|
26
|
+
{#each section.data.rows as row, _i}
|
|
27
27
|
<tr class="hover:bg-gray-50 dark:hover:bg-gray-800 transition-colors">
|
|
28
28
|
{#each row.columns as column, j}
|
|
29
29
|
<td class="px-3 py-2 text-sm {j === 0 ? 'font-medium text-gray-900 dark:text-gray-100' : 'text-gray-600 dark:text-gray-400'}">
|
|
@@ -128,7 +128,7 @@
|
|
|
128
128
|
{#if readonly}
|
|
129
129
|
<!-- View Mode: Clean minimal layout -->
|
|
130
130
|
<div class="space-y-6">
|
|
131
|
-
{#each Object.entries(fieldGroups) as [
|
|
131
|
+
{#each Object.entries(fieldGroups) as [_groupName, fields]}
|
|
132
132
|
{#each [fields] as fieldList}
|
|
133
133
|
{@const importantFields = fieldList.filter((f) =>
|
|
134
134
|
['id', 'title', 'priority', 'status'].includes(f.id)
|
|
@@ -226,7 +226,7 @@
|
|
|
226
226
|
</span>
|
|
227
227
|
</h3>
|
|
228
228
|
<div class="space-y-3">
|
|
229
|
-
{#each value as item,
|
|
229
|
+
{#each value as item, _index}
|
|
230
230
|
<div
|
|
231
231
|
class="bg-gray-50 dark:bg-gray-800 rounded-lg p-4 border border-gray-200 dark:border-gray-700"
|
|
232
232
|
>
|