From bfebf200b754cf3dddffc7ca5aa3908962ba725f Mon Sep 17 00:00:00 2001 From: Kpa-clawbot Date: Mon, 25 May 2026 22:16:17 -0700 Subject: [PATCH] =?UTF-8?q?fix(#1375):=20scope-stats=20fetch=20path=20?= =?UTF-8?q?=E2=80=94=20drop=20duplicate=20/api=20prefix=20(Scopes=20tab=20?= =?UTF-8?q?JSON.parse=20fix)=20(#1379)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## What Drop the leading `/api` from the Scopes-tab `scope-stats` fetch in `public/analytics.js`. The `api()` helper already prefixes `/api`; passing `/api/scope-stats` produced a runtime URL of `/api/api/scope-stats`, which 404s, falls through to the SPA HTML, and crashes the Scopes tab with `JSON.parse: unexpected character`. Single-line behavior change. ## Why `api()` (defined earlier in the same file) prepends `/api`. Every other caller in `public/analytics.js` correctly passes a helper-relative path (`/observers`, `/nodes`, …). The Scopes loader was the lone offender. The same fix originally landed on the PR #915 branch (commit `2fd22cee`) but that branch never merged, so the bug resurfaced on subsequent rebases. The Scopes tab is therefore broken on production today — open `/analytics` → Scopes and the panel never renders. ## TDD - Red commit `b1fbc5601a985f20eb0ffee9181b7df5333248ca` adds `test-issue-1375-scope-stats-fetch.js`, which reads `public/analytics.js` and asserts: - ZERO matches of literal `api('/api/scope-stats'` (regression guard). - Exactly one match of `api('/scope-stats'` (positive — fix present). - Green commit edits the loader to drop the duplicate `/api`. - Test wired into `.github/workflows/deploy.yml` next to the existing `test-issue-*` entries. ## Manual verification After deploy, open `https://analyzer.00id.net/analytics`, click **Scopes**: panel renders cards instead of throwing a JSON parse error in DevTools console. Fixes #1375 --------- Co-authored-by: openclaw-bot --- .github/workflows/deploy.yml | 1 + public/analytics.js | 2 +- test-issue-1375-scope-stats-fetch.js | 50 ++++++++++++++++++++++++++++ 3 files changed, 52 insertions(+), 1 deletion(-) create mode 100644 test-issue-1375-scope-stats-fetch.js diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index 4a5dec44..051f1f98 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -109,6 +109,7 @@ jobs: node test-issue-1356-map-a11y.js node test-issue-1360-pill-letter-count.js node test-issue-1364-pill-no-clamp.js + node test-issue-1375-scope-stats-fetch.js - name: Verify proto syntax run: | diff --git a/public/analytics.js b/public/analytics.js index d6ec1c9f..a198a6bb 100644 --- a/public/analytics.js +++ b/public/analytics.js @@ -3986,7 +3986,7 @@ function destroy() { _stopRolesRefresh(); _stopScopesRefresh(); _analyticsData = if (loadingEl) loadingEl.style.display = ''; try { // Fix 4: use api() instead of raw fetch() - var data = await api('/api/scope-stats?window=' + encodeURIComponent(w), { ttl: 30000 }); + var data = await api('/scope-stats?window=' + encodeURIComponent(w), { ttl: 30000 }); if (loadingEl) loadingEl.style.display = 'none'; if (data.error) { var cardsEl2 = document.getElementById('scopes-cards'); diff --git a/test-issue-1375-scope-stats-fetch.js b/test-issue-1375-scope-stats-fetch.js new file mode 100644 index 00000000..a68b46a4 --- /dev/null +++ b/test-issue-1375-scope-stats-fetch.js @@ -0,0 +1,50 @@ +/** + * #1375 — regression(analytics): Scopes tab fetches `/api/api/scope-stats` + * (duplicate prefix) → 404 → SPA HTML → JSON.parse error. + * + * The `api()` helper already prepends `/api`. Other callers in + * public/analytics.js correctly pass `/scope-stats` style relative paths; + * the Scopes loader was the lone offender passing `/api/scope-stats`, + * producing the doubled prefix at runtime. + * + * Fix: drop the leading `/api` from the Scopes-tab call so the helper + * builds `/api/scope-stats?window=…`. + * + * Originally landed on the PR #915 branch (commit 2fd22cee) but that + * branch never merged, so the bug resurfaced in subsequent rebases. + */ +'use strict'; + +const fs = require('fs'); +const path = require('path'); + +let passed = 0, failed = 0; +function assert(cond, msg) { + if (cond) { passed++; console.log(' ✓ ' + msg); } + else { failed++; console.error(' ✗ ' + msg); } +} + +const src = fs.readFileSync( + path.join(__dirname, 'public', 'analytics.js'), 'utf8'); + +console.log('\n=== #1375: Scopes tab scope-stats fetch path ==='); + +// Regression guard: the buggy doubled-prefix form must never reappear. +const badRe = /api\(\s*['"]\/api\/scope-stats/g; +const badMatches = src.match(badRe) || []; +assert(badMatches.length === 0, + "ZERO `api('/api/scope-stats'` occurrences in analytics.js " + + '(regression guard for doubled /api prefix)'); + +// Positive: the corrected, helper-relative form is present exactly once. +const goodRe = /api\(\s*['"]\/scope-stats/g; +const goodMatches = src.match(goodRe) || []; +assert(goodMatches.length === 1, + "Exactly one `api('/scope-stats'` call exists (the fixed loader) — " + + 'found ' + goodMatches.length); + +console.log('\n=== Summary ==='); +console.log(' Passed: ' + passed); +console.log(' Failed: ' + failed); +console.log('\n#1375 ' + (failed === 0 ? 'PASS' : 'FAIL')); +process.exit(failed === 0 ? 0 : 1);