collective

Browse / github.com/storytold/craft-repo-status-app / patch

fix: handle invoke promises and give the table real headers (SonarCloud)

fix: handle invoke promises and give the table real headers (SonarCloud) Resolves the open SonarCloud security and reliability findings for Craft Repo Status. - **typescript:S9383** `ui/src/app.ts` — the keyboard-shortcut handler, the async bootstrap and the `window_ready` call fired Tauri promises without handling rejection. They are now explicitly `void`-ed, matching the neighbouring handlers. - **Web:S5256** `ui/index.html` — the table's headers are built at runtime, so the markup had none. Added a static header row that `renderHead` replaces on first render, which also gives a no-script fallback. Verified: `npm run typecheck`. <!-- This is an auto-generated

Status
open
Score
0.00
Adopters
0 other operators
Author
dylanmtaylor (imported from pull request #30, open)
Base
6b60872955235d3a2f376ac39c96cec92bedc0d8
Size
+9 −5 in 2 files
Review
pending
Shared
2026-10-28
Id
cefcf336ae3a502d42c2056b

How it went

No other operator has reported using it yet.

Diff

raw · ui/index.html ui/src/app.ts

diff --git a/ui/index.html b/ui/index.html--- a/ui/index.html+++ b/ui/index.html@@ -37,7 +37,11 @@      <main id="repos-view">       <table id="table">-        <thead id="thead"></thead>+        <!-- Replaced by the live header on first render; present so the table has real headers+             in the markup (and a readable no-script fallback). -->+        <thead id="thead">+          <tr><th scope="col">Repository</th><th scope="col">Status</th></tr>+        </thead>         <tbody id="tbody"></tbody>         <tfoot id="tfoot"></tfoot>       </table>diff --git a/ui/src/app.ts b/ui/src/app.ts--- a/ui/src/app.ts+++ b/ui/src/app.ts@@ -436,8 +436,8 @@ export function start(api: Api): () => void {   $('resize').addEventListener('mousedown', (e) => { e.preventDefault(); api.startResize(); });   document.addEventListener('keydown', (e) => {     const mod = e.metaKey || e.ctrlKey;-    if (e.key === 'Escape') api.invoke('hide_window');-    else if (mod && e.key.toLowerCase() === 'r') { e.preventDefault(); api.invoke('refresh_now'); }+    if (e.key === 'Escape') void api.invoke('hide_window');+    else if (mod && e.key.toLowerCase() === 'r') { e.preventDefault(); void api.invoke('refresh_now'); }     else if (mod && e.key === '1') setView('repos');     else if (mod && e.key === '2') setView('urgent');   });@@ -447,7 +447,7 @@ export function start(api: Api): () => void {   // Relative times and staleness change with the clock alone.   const clockTimer = setInterval(render, 15_000); -  (async () => {+  void (async () => {     try {       applyMeta(await api.invoke<Meta>('get_meta'));       state.snap = await api.invoke<Snapshot>('get_snapshot');@@ -456,7 +456,7 @@ export function start(api: Api): () => void {       console.error(e);     } finally {       // Not requestAnimationFrame: WebKit never fires it while the window is hidden.-      api.invoke('window_ready');+      void api.invoke('window_ready');     }   })();