mbproxy: close out the dashboard code-review minor findings

Resolves the remaining Minor items from the 2026-05-15 review so the
web-UI dashboard work has no open follow-ups: a real-HubConnection
end-to-end test for the SignalR feed, stable mbproxy.admin.broadcast.*
log-event names, keyboard/aria accessibility on the fleet table,
frontend JS hardening (URL-decode guard, NaN guards, shared util.js),
reconciler<->capture-registry coverage, throwing-sink and embedded-asset
tests, broadcaster polish, and a soft upper bound on AdminPushIntervalMs.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
Joseph Doherty
2026-05-16 16:36:39 -04:00
parent 374eecd205
commit 0308490aef
21 changed files with 576 additions and 67 deletions
+21 -6
View File
@@ -7,9 +7,20 @@
(function () {
// ── PLC name from the URL path: /plc/{name} ────────────────────────────
const plcName = decodeURIComponent(location.pathname.replace(/^\/plc\//, ''));
// decodeURIComponent throws URIError on a malformed %-escape; fall back to the
// raw path segment and flag the failure so the boot path shows a notice instead
// of letting the whole script abort on the very first statement.
const rawSegment = location.pathname.replace(/^\/plc\//, '');
let plcName, plcNameError = false;
try {
plcName = decodeURIComponent(rawSegment);
} catch {
plcName = rawSegment;
plcNameError = true;
}
const $ = (id) => document.getElementById(id);
const { escapeHtml } = window.mbproxyUtil;
document.title = `mbproxy — ${plcName}`;
$('crumb-name').textContent = plcName;
@@ -20,9 +31,6 @@
if (n === null || n === undefined) return '—';
return n.toLocaleString('en-US');
}
function escapeHtml(s) {
return String(s).replace(/[&<>]/g, c => ({ '&': '&amp;', '<': '&lt;', '>': '&gt;' }[c]));
}
function hex4(n) { return '0x' + (n & 0xffff).toString(16).toUpperCase().padStart(4, '0'); }
// First debug-row cell: the tag's friendly name (when configured) over its PDU
@@ -37,7 +45,7 @@
}
function formatAge(sec) {
if (sec === null || sec === undefined) return '—';
if (!Number.isFinite(sec) || sec < 0) return '—';
if (sec < 1) return 'now';
if (sec < 60) return sec.toFixed(1) + 's';
const m = Math.floor(sec / 60);
@@ -299,5 +307,12 @@
}
}
document.addEventListener('DOMContentLoaded', connect);
document.addEventListener('DOMContentLoaded', () => {
if (plcNameError) {
showNotice('The PLC name in this URL could not be decoded — the address is ' +
'malformed. Return to the fleet page and open the PLC from the table.');
return;
}
connect();
});
})();