Shell 1.9.1: a module update actually runs the updated code

AB updated the Timers module to 1.5.0 and its new LTC capture-tool
self-build never ran: mountModule re-required the module's server entry,
and Node's require cache handed back the OLD code — the catalog update
had swapped the folder on disk, but the process kept executing what it
loaded at startup until ProdDash itself restarted. The manifest, read
fresh from disk, meanwhile reported the new version, so the admin page
claimed an update that functionally hadn't happened. Every module update
to date has silently depended on a follow-up app restart.

mountModule now drops the module directory's entries from require.cache
before requiring, so every (re)mount runs the files on disk — nested
requires (ltc-listener.js and friends) included. Modules already must
come up clean on re-init per the module guide, so config-change remounts
see the same contract, minus the trap.

Verified with a probe module in a scratch data dir: change a nested
require on disk, PUT config to remount, the route serves the new code
(it served the stale code before the fix). The Timers module re-inits
cleanly across repeated fresh-code remounts.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
Waters Church
2026-09-27 10:26:38 -04:00
co-authored by Claude Fable 5
parent 21519dd3da
commit 076e583daf
2 changed files with 13 additions and 2 deletions
+1 -1
View File
@@ -1,6 +1,6 @@
{ {
"name": "proddash", "name": "proddash",
"version": "1.9.0", "version": "1.9.1",
"private": true, "private": true,
"description": "Modular production dashboard for Waters Church \u2014 zero-dependency Node server + vanilla client", "description": "Modular production dashboard for Waters Church \u2014 zero-dependency Node server + vanilla client",
"license": "UNLICENSED", "license": "UNLICENSED",
+12 -1
View File
@@ -373,7 +373,18 @@ function mountModule(id) {
const log = makeLog(id); const log = makeLog(id);
try { try {
const serverPath = path.join(man.dir, man.server); const serverPath = path.join(man.dir, man.server);
// Re-require fresh config on every (re)mount; the module code itself stays cached. // Drop the module's code from the require cache so every (re)mount runs
// the files on disk. Without this a module UPDATE swaps the folder while
// the process keeps executing the old code until ProdDash itself
// restarts — the manifest (read fresh from disk) then reports a version
// the running code isn't, and anything new in the update silently
// doesn't happen. Modules already have to come up clean on re-init
// (module guide), so a fresh load on config remounts is the same
// contract without the trap.
const cachePrefix = man.dir.endsWith(path.sep) ? man.dir : man.dir + path.sep;
for (const key of Object.keys(require.cache)) {
if (key.startsWith(cachePrefix)) delete require.cache[key];
}
const mod = require(serverPath); const mod = require(serverPath);
const config = effectiveConfig(id); const config = effectiveConfig(id);
// What a module needs to reach the rest of ProdDash — another module's // What a module needs to reach the rest of ProdDash — another module's