Repository navigation
Conversation
|
Preview deployed: https://freifunk.github.io/meshviewer/pr-preview/pr-507/ Updated for commit 42b6917. |
Mechanical fix from eslint's prefer-const (via typescript-eslint's recommended preset), applied with --fix. Assisted-by: claude:opus-5.5
Implementations may omit trailing parameters of the callback type they satisfy, so the placeholder _a/_b/_n/_d/_nodeDict arguments are not needed. Use optional catch binding where the error is ignored. Assisted-by: claude:opus-5.5
Drop casts and non-null assertions that do not change the type, and use an explicit type argument for L.DomUtil.create instead of a cast. Assisted-by: claude:opus-5.5
{} admits any non-nullish value; use object / Record<string, unknown>
instead. Drop union members that are swallowed by unknown or string.
Assisted-by: claude:opus-5.5
- load: show a loader error when config.json is not valid JSON; before, the rejection was unhandled and the loader spun forever. - main: catch failures of the periodic data update so a network blip does not raise an unhandled rejection every minute. - language, infobox/location: log failed locale and reverse-geocoding requests. - map, domUtils: mark fire-and-forget promises with void; geo layer errors are already reported through onError. Assisted-by: claude:opus-5.5
Covers offline, non-ok HTTP status, invalid JSON in config.json and the happy path, where config.json is merged onto the defaults before main() starts. Assisted-by: claude:opus-5.5
287cc29 to
42b6917
Compare
rotanid
left a comment
There was a problem hiding this comment.
A few findings to consider:
1. HTML injection / XSS risk in lib/load.ts (showLoaderError)
In lib/load.ts:
let config: unknown;
try {
config = await configResponse.json();
} catch (e) {
showLoaderError(
"config.json is not valid JSON:<br>" + (e instanceof Error ? e.message : String(e)),
"or report to your community",
);
return;
}showLoaderError injects message directly into innerHTML:
const showLoaderError = (message: string, hint = "") => {
document.querySelector(".loader")!.innerHTML =
message +
"<br><br>" +
'<button onclick="location.reload(true)" class="btn text" aria-label="Try to reload">' +
"Try to reload" +
"</button><br>" +
hint;
};In V8/Chromium, when response.json() fails to parse JSON, SyntaxError.message includes a snippet of the unexpected token input (e.g. Unexpected token '<', "<img src=x "... is not valid JSON). If config.json is served from an untrusted origin, or a captive portal / proxy returns an HTML page with HTTP 200, unescaped HTML tags will be parsed and rendered by .innerHTML.
Suggestion:
Escape e.message using the existing escape() helper from lib/utils/escape.ts:
import { escape } from "./utils/escape.js";
// ...
showLoaderError(
"config.json is not valid JSON:<br>" + escape(e instanceof Error ? e.message : String(e)),
"or report to your community",
);2. void operator does not catch rejected promises in lib/utils/domUtils.ts
In lib/utils/domUtils.ts:
const enter =
fel.requestFullscreen?.bind(fel) ?? fel.webkitRequestFullScreen?.bind(fel) ?? fel.mozRequestFullScreen?.bind(fel);
void enter?.();
// ...
if (exit) {
void exit();
}The void keyword discards the return value of an expression to satisfy @typescript-eslint/no-floating-promises, but it does not catch runtime Promise rejections. If requestFullscreen() or exitFullscreen() rejects (e.g. inside an <iframe> lacking allow="fullscreen", or if denied by browser permission policies), an unhandledrejection event is still fired on window.
Suggestion:
Attach a .catch() to explicitly handle the rejection:
enter?.().catch(console.warn);
// ...
exit?.().catch(console.warn);3. Redundant union Node | object in lib/utils/nodeUtils.ts
In lib/utils/nodeUtils.ts:
export const hasLocation = function hasLocation(data: Node | object)
export const hasUplink = function hasUplink(data: Node | object)Since Node is an interface, it is already a subtype of object, so Node | object collapses to just object.
Suggestion:
Simplify the parameter type to data: object or data: Record<string, unknown>.
Review conducted with AI assistance (Google Antigravity / Gemini 3.8 Flash).
Description
Fixes some findings from a one-off typescript-eslint 8 run (
recommendedTypeChecked) overlib/, reducing them from 601 to 337. Almost all of the remaining ones areany/no-unsafe-*. One commit per category:let→const._a,_b,_nodeDictthat were only there to match an interface (TypeScript allows implementations to omit trailing parameters);catch {}where the error isignored.
!that don't change the type;L.DomUtil.create<"canvas">(…)instead ofas HTMLCanvasElement.{}and redundant unions:object/Record<string, unknown>instead of{}; drop union members swallowed byunknownorstring.config.jsonthat is served but not valid JSON used to leave the loader spinning forever with an unhandled rejection. It now shows "config.json is not valid JSON" with the parse error.void(geo errors are already reported viaonError).Intentionally left out:
prefer-constwhere variables are declared up front and assigned later (forcegraph.ts,gui.ts,map.ts); fixing them needs code reordering.any/no-unsafe-*(~310): real typing work, better done incrementally.no-base-to-string,unbound-method,require-await: false positives or test mocks.Motivation and Context
The eslint config did only lint
.js/.mjsfiles, so none of the 70.tsfiles inlib/are linted today.@typescript-eslint/parserwas listed indevDependenciesbut never wired into the config.typescript-eslint doesn't support TypeScript 7 yet (peer range
<6.1.0, and TS 7 no longer ships the classic compiler API it needs), so adding it would block the TS 7 bump. This PR only cleans up the code so that enabling it later is a smaller step.How Has This Been Tested?
tsc --noEmit,npm run build,npm run lintandvitest(72 tests) pass.--no-save, not committed) confirms the listed rules are clean.config.jsonpath checked with a throwaway unit test.Screenshots/links:
None, no visual changes. Only the error text shown for an invalid
config.jsonis new.Checklist: