Skip to content

v0.20.4

2026-05-06

  • [x] Hotfix: production frontend bundle was shipping with an empty moduleLoaders = {}, so optional modules (system8, genai, scenes, vtt-assets) never had their client register() called. Symptom: system8 showed up in /api/systems and the Campaign modal but not in the character sheet system dropdown (which is the only consumer that depends on the client-side moduleRegistry). Root cause: scripts/install-hosted-modules-frontend.sh correctly copies hosted modules into apps/web/node_modules/@purple-cat-games/... (since v0.20.3), but apps/web/vite-plugin-modules.ts's package-name resolver only walked up from the repo root looking for node_modules directories. apps/web/node_modules is a child of the repo root, so the walk-up never found it and resolveClientEntry silently returned null for every hosted module. v0.20.3 unblocked CI's Rollup peer-dep crash but didn't fix the underlying resolution miss — the bundle just started building successfully with no modules wired up. Fix: replace the walk-up with an explicit, ordered search list (apps/web/node_modules first, then repo-root node_modules), exported as getModuleSearchRoots for testability. Also: purpleCatModulesPlugin now throws when modules declared in modules.hosted.json can't be resolved (defaults to failOnUnresolved: true for hosted config, false for modules.local.json so dev builds with partial configs still succeed). Two silent-skip-shipped-broken-bundle incidents in two months — the build must fail loudly when this happens. Added 5 new tests in apps/web/vite-plugin-modules.test.ts covering: search-root order pinned, hosted-module resolution under apps/web/node_modules (regression test for this exact bug), missing-package error reporting includes searched paths, apps/web wins over repo root when both have the same package, modules with no client entry don't trigger failure, and present-but-broken client entries are flagged.