feat(modules): the client registry, window.__rg and the chunk's script injection
Phase 2, PR 7 of docs/website/MODULE_SYSTEM.md 2.7 — the client half's
delivery. A module's prebuilt chunk is served, injected, handed core's React
and its UI kit, and its routes are rendered by App.jsx. The registry is empty
on a bare core, so nothing an operator can see changes.
Client:
- modules/registry.js — registerRoutes/registerNav/registerFeatureProvider,
with the URL namespace written by core, never by the module
- modules/shared.js — window.__rg: React, react-dom/client, react-router-dom,
react/jsx-runtime, the registry, the seven-member UI kit and the request
primitive, frozen
- App.jsx reads routesFor for all three areas; nav consumption is PR 8
- main.jsx publishes the global, then mounts on DOMContentLoaded
Server:
- the loader validates client.entry and publishes clientChunks() and
clientEntryUrls(); an entry in the module root is rejected, because the
directory it sits in is what gets served
- app.js mounts each chunk at /modules/<id>/ behind the module's state guard
with no-cache; anything else under /modules is a 404, not the SPA shell
- htmlShell injects the tag before </body>, so core's bundle runs first
wherever a bundler puts it
Found by loading a real chunk in a browser, and fixed here: core mounted before
any module chunk had evaluated, because document.readyState during a deferred
script is 'interactive', not 'loading'. Every test passed against that build.
The smoke is written down in MODULE_API.md 7.7.
933 server tests (+23), 123 client tests (+14). routes.manifest.json unchanged
at 230 routes; the OpenAPI spec regenerates byte-identical.
Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
14
client/src/modules/version.js
Normal file
14
client/src/modules/version.js
Normal file
@@ -0,0 +1,14 @@
|
||||
// The client's copy of MODULE_API_VERSION. It must equal the server's
|
||||
// (server/src/modules/version.js) — the two halves version ONE contract
|
||||
// (docs/website/MODULE_API.md §1.1), and a module checks whichever half it is
|
||||
// talking to: `coreApi` against the server's at load time, `window.__rg.version`
|
||||
// against the client's before it registers anything.
|
||||
//
|
||||
// Duplicated rather than fetched, and that is deliberate. The value has to be on
|
||||
// `window.__rg` before the first module chunk evaluates, which is earlier than
|
||||
// any network round trip could answer — a fetched version would mean either an
|
||||
// await before render or a module reading `undefined`. The cost of the copy is
|
||||
// that the two files can drift, so a test asserts they agree
|
||||
// (client/test/moduleRegistry.test.js) rather than trusting a bump to remember
|
||||
// both.
|
||||
export const MODULE_API_VERSION = '1.0.0'
|
||||
Reference in New Issue
Block a user