diff --git a/Plugin/unraid/Tools/js_check.sh b/Plugin/unraid/Tools/js_check.sh new file mode 100755 index 0000000..1abb7a0 --- /dev/null +++ b/Plugin/unraid/Tools/js_check.sh @@ -0,0 +1,337 @@ +#!/bin/bash +# ============================================================================================== +# ============================= WebGUI JavaScript Checker ====================================== +# ============================================================================================== +# +# PURPOSE +# ============================================================================================== +# Finds the two JavaScript faults this plugin has actually shipped, neither of which any syntax +# check can see, because both are runtime behaviour: +# +# 1. An identifier declared inside one function and referenced from another. Throws +# ReferenceError on every call, kills the rest of the render, and passes php -l and +# node --check without complaint. +# +# 2. A fetch chain ending in an empty catch. Not error handling — error deletion. The request +# fails, nothing renders, nothing is logged, and the surface sits on "Loading…" forever. +# +# The two compound: on 2026-08-20 a cross-scope ReferenceError in the mesh chat was swallowed by +# an empty catch on every render. It presented as "the chat takes a minute to load" — the minute +# was the poller's backoff — and hours went into profiling PHP that was never slow. Once a catch +# reported the error, the fault named itself in one line. +# +# ============================================================================================== +# OPERATIONAL MODEL +# ============================================================================================== +# +# 1. Extract the JS from each +FIX + fails=0 + echo "── self-test ────────────────────────────────────────────────" + if scan_scope "$WORK/fixture.php" | grep -q "paletteMap"; then + echo " cross-scope detector PASS" + else + echo " cross-scope detector FAIL — known fault not reported"; fails=1 + fi + if scan_catches "$WORK/fixture.php" | grep -q "empty catch"; then + echo " empty-catch detector PASS" + else + echo " empty-catch detector FAIL — known fault not reported"; fails=1 + fi + # A clean file must stay clean, or the detector is merely reporting everything. + cat > "$WORK/clean.php" <<'CLN' + +CLN + if [[ -z "$(scan_scope "$WORK/clean.php")$(scan_catches "$WORK/clean.php")" ]]; then + echo " clean file stays clean PASS" + else + echo " clean file stays clean FAIL — false positive on correct code"; fails=1 + fi + echo "─────────────────────────────────────────────────────────────" + [[ "$fails" -eq 0 ]] && echo "self-test OK" || echo "SELF-TEST FAILED — do not trust a clean scan" + exit "$fails" +fi + +# ── Scan ────────────────────────────────────────────────────────────────────────────────────── +if [[ "${#FILES[@]}" -eq 0 ]]; then + while IFS= read -r p; do FILES+=("$p"); done < <( + find "$PLUGIN_DIR/pages" "$PLUGIN_DIR/include" -maxdepth 1 -name '*.php' 2>/dev/null + [[ -f "$PLUGIN_DIR/Varaverk.page" ]] && echo "$PLUGIN_DIR/Varaverk.page" + ) +fi + +found=0 +for f in "${FILES[@]}"; do + [[ -f "$f" ]] || continue + grep -q "/dev/null || continue + out="" + [[ "$DO_SCOPE" == true ]] && out+="$(scan_scope "$f")" + if [[ "$DO_CATCH" == true ]]; then + c="$(scan_catches "$f")" + [[ -n "$c" ]] && out+=$'\n'"$c" + fi + out="$(echo "$out" | sed '/^$/d')" + if [[ -n "$out" ]]; then + echo "### $(basename "$f")" + echo "$out" + found=$(( found + $(echo "$out" | grep -c '^ line') )) + fi +done + +echo +if [[ "$found" -eq 0 ]]; then + echo "clean — no cross-scope references, no silent fetch catches" +else + echo "$found finding(s). Cross-scope hits over-report: regex literals, nested-function" + echo "parameters, destructured for-of and multi-declarator const each read as undeclared." + echo "Read each before changing anything." +fi +exit $(( found > 0 ? 1 : 0 ))