Files
hermes-config/skills/self-hosting/shop-pro-quote/references/js-debugging.md
T
2026-07-12 10:17:17 -04:00

3.8 KiB

ShopProQuote JS Debugging Patterns

Silent module load failure from SyntaxError

Symptom: A feature is "stuck loading" despite the HTML being correct and the backend responding. The page shows a permanent "Loading..." state.

Root cause: A syntax error in a <script>-loaded JS file prevents the ENTIRE module from executing. Common in ShopProQuote because JS files are loaded as regular scripts (not ES modules with isolated scopes).

Example from dashboard briefing (June 2026):

// Line ~3926
const briefingText = dsResult.choices?.[0]?.message?.content?.trim() || '';
// ... later, line ~3944, same function scope:
const briefingText = contentDiv.textContent || '';  // 💥 SyntaxError: redeclaration of const

JavaScript refuses to parse the file, window.generateDailyBriefing is never defined, the inline fallback in index.html polls forever for it, and the user sees "Loading briefing..." permanently.

Debugging steps

  1. Check if the function exists at all:

    // In browser console
    typeof window.generateDailyBriefing  // 'undefined' = module never loaded
    
  2. Syntax-check the file (if Node.js available):

    node -c dashboard.js
    
  3. Without Node.js, grep for duplicate declarations:

    # Find duplicate const declarations in the same function
    sed -n '/async function generateDailyBriefing/,/^    }/p' dashboard.js | grep -P "^\s*const \w+ =" | awk '{print $2}' | sort | uniq -d
    
  4. Check browser console — a SyntaxError appears but users often miss it.

Proxy verification pattern

When an AI feature relies on an nginx proxy, verify the proxy works independently of the frontend:

curl -k -X POST https://127.0.0.1/deepseek/v1/chat/completions \
  -H "Content-Type: application/json" \
  -d '{"model":"deepseek-v4-flash","messages":[{"role":"user","content":"test"}],"max_tokens":10}'

This separates "proxy broken" from "JS broken."

"Button does nothing" — element existence guard pattern

When clicking a submit button produces literally zero visible effect (no spinner, no error, no console message beyond a hidden TypeError), the most likely cause is that one or more DOM elements the handler reads from are null. The form element exists in the HTML, but at the time the handler fires, it's not in the DOM (dynamic insertion timing, modal re-opening, or form.reset() clearing dynamic content).

Defensive pattern

Instead of directly chaining .value on document.getElementById() calls, capture each element reference first, check for null, and log which ones are missing:

async function handleCreateTask() {
    console.log('🔍 handleCreateTask() called');
    const titleEl = document.getElementById('new-task-title');
    const dueDateEl = document.getElementById('new-task-due-date');
    const dueTimeEl = document.getElementById('new-task-due-time');
    
    if (!titleEl || !dueDateEl || !dueTimeEl) {
        console.error('❌ Missing form elements:', {
            title: !!titleEl, dueDate: !!dueDateEl, dueTime: !!dueTimeEl
        });
        showNotification('Form elements not found. Please refresh the page.', true);
        return;
    }
    
    const title = titleEl.value.trim();
    // ... rest of handler
}

This pattern accomplishes three things simultaneously:

  1. The console.log at line 1 proves the handler even fired (vs HTML5 validation blocking submit)
  2. The null checks pinpoint exactly which element is missing
  3. The early return with notification gives the user a visible message instead of silent failure

Why this beats try/catch alone

A try/catch wrapping the handler body catches the TypeError, but you don't know WHICH element caused it without the logged boolean map. And you can't show a meaningful error message — just "something went wrong."