refactor(modules): move core's UO page content behind the two slots
Core declares site.footer.status and admin.users.detail in main.jsx and fills both itself, under owner id `core` -- the client twin of registries.registerCore() and the same trick useShardFlags already uses. The rendered page is unchanged; what changes is that the content now arrives the way a module's will. The footer's Shard Status link becomes ShardStatusLink.jsx, and UserDetail's six UO sections become UserShardSections.jsx. Both are files rather than inline markup so that the client half of phase 3 deletes a registration and a file instead of editing a core page under extraction pressure -- which is also what proves the mechanism before anything depends on it. The user-detail slot is handed userId and not scope. api.admin.userShard is a UO binding that leaves core with the client half, so a slot passing it would hand a module something core is about to delete; an extension builds its own client for the routes it registered at the other end. Core's own fill now does exactly what the module will. Verified in a browser against a real chunk (MODULE_API.md 7.7): a throwaway module fills both slots and renders its own label and target in the footer with core's linkStyle, and receives userId on the admin page; a deliberate render failure is contained to that one spot with the slot named in the console; core's own fills leave the pages byte-identical to before; and with no module installed both slots render nothing. Zero CSP reports throughout. Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -3,8 +3,10 @@ import { createRoot } from 'react-dom/client'
|
||||
import { BrowserRouter } from 'react-router-dom'
|
||||
import App from './App.jsx'
|
||||
import { publishSharedDependencies } from './modules/shared.js'
|
||||
import { registerFeatureProvider } from './modules/registry.js'
|
||||
import { declareSlot, registerExtension, registerFeatureProvider } from './modules/registry.js'
|
||||
import { useShardFlags } from './lib/useShardFeatures.js'
|
||||
import ShardStatusLink from './components/ShardStatusLink.jsx'
|
||||
import UserShardSections from './routes/admin/views/UserShardSections.jsx'
|
||||
import './styles/theme.css'
|
||||
|
||||
// Publish window.__rg BEFORE rendering and before any module chunk evaluates.
|
||||
@@ -28,6 +30,34 @@ publishSharedDependencies()
|
||||
// for them by the name they will always have had.
|
||||
registerFeatureProvider('core', 'uo', useShardFlags)
|
||||
|
||||
// ── Extension slots (MODULE_API.md §3.7) ───────────────────────────────────
|
||||
//
|
||||
// Declared HERE, in core's own bundle, which is what makes the ordering a fact
|
||||
// rather than a hope: module chunks are deferred scripts the shell injects after
|
||||
// this one (§3.1), so a module can never reach registerExtension before the slot
|
||||
// it names exists. "Unknown slot" therefore always means a typo or a version
|
||||
// skew, never a load-order accident — which is why that case throws.
|
||||
//
|
||||
// Both slots are named for a PLACE, not for a meaning. `site.footer.status` is
|
||||
// the spot in the footer's info row, not a declaration that core knows what a
|
||||
// game server's status is; the label, the target and whether anything renders at
|
||||
// all belong to whoever fills it. A slot typed by its content would put game
|
||||
// semantics back into core, which is the thing Phase 3 takes out.
|
||||
declareSlot('site.footer.status')
|
||||
// Deliberately the same name as the server's slot (MODULE_API.md §2.4): one
|
||||
// resource, one extension point, two halves. The module with routes under
|
||||
// /api/v1/admin/users/:id is the module with something to show on that page.
|
||||
declareSlot('admin.users.detail')
|
||||
|
||||
// And core fills both itself, under owner id `core`, with the components that
|
||||
// were inline in SiteFooter.jsx and UserDetail.jsx until this slice. The page
|
||||
// renders exactly what it rendered before, and the mechanism is exercised by
|
||||
// core's own content from the day it lands rather than first proved by the
|
||||
// change that depends on it. Slice 3 deletes these three lines and the two files
|
||||
// they name, and the module registers the same two slots on its way in.
|
||||
registerExtension('core', 'site.footer.status', ShardStatusLink)
|
||||
registerExtension('core', 'admin.users.detail', UserShardSections)
|
||||
|
||||
// Render on DOMContentLoaded rather than immediately, and that is the one line
|
||||
// of core's boot the module system changes.
|
||||
//
|
||||
|
||||
Reference in New Issue
Block a user