feat(modules): a third slot, nav icons, and api.BASE (phase 3, slice 3)
The three things core owes the client half before it can leave, all additive, all MODULE_API 1.2.0 → 1.3.0. `player.invite.accepted` is the third extension slot. Core's invite page owned a UO game-account step — it read a `gameAccountSignup` flag out of core's own settings and posted to a shard route — and an invite is a core concept that staff receive too, so the page stays and its optional next step becomes a slot. Named for the place, like the other two. Whether there is a step at all is the filling module's call, made from data core does not have; core keeps the shell, the skip control and the destination. `icon` on a nav item, because without it the six extracted UO rows would have been the only text-only entries in a sidebar where every other row has a glyph. Core supplies no fallback — an invented one is core making a presentation choice for content it knows nothing about. `icon` was already among the fields an override may not touch, so the concept predates a module being able to send one. `api.BASE` was in §3.5 from the first draft and never actually published. `request` is fetch-only, so an EventSource builds its own URL, and the shard's live feed is two of them; the alternative is a module hardcoding `/api/v1`, which asserts something about core that core has not promised. `AcceptInvite` is the one legitimate reader of `extensionFor` outside Slot.jsx: the answer decides a NAVIGATION, not a decoration. Decoration goes inside `<Slot wrap>`, which is why `hasExtension` stayed deleted. Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -3,10 +3,7 @@ import { createRoot } from 'react-dom/client'
|
||||
import { BrowserRouter } from 'react-router-dom'
|
||||
import App from './App.jsx'
|
||||
import { publishSharedDependencies } from './modules/shared.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 { declareSlot } from './modules/registry.js'
|
||||
import './styles/theme.css'
|
||||
|
||||
// Publish window.__rg BEFORE rendering and before any module chunk evaluates.
|
||||
@@ -17,18 +14,12 @@ import './styles/theme.css'
|
||||
// (docs/website/MODULE_API.md §3.2).
|
||||
publishSharedDependencies()
|
||||
|
||||
// Core registers through the same seam a module uses, and registers FIRST — the
|
||||
// client twin of the server's `registries.registerCore()` (MODULE_SYSTEM.md
|
||||
// §1.9). The ten shard-gated rows in the public header are core's only because
|
||||
// Phase 3 has not moved them yet; routing them through the registry now means
|
||||
// SiteHeader holds one mechanism instead of two, and the extraction becomes a
|
||||
// deletion rather than a rewrite made under extraction pressure.
|
||||
//
|
||||
// The owner id is `core`, which is what a nav row with no `moduleId` resolves
|
||||
// against (modules/featureGate.js). The namespace is `uo`, so a module that
|
||||
// wants to read these flags — the `uo` module itself, once it owns them — asks
|
||||
// for them by the name they will always have had.
|
||||
registerFeatureProvider('core', 'uo', useShardFlags)
|
||||
// Core registered a feature provider here until slice 3, under owner id `core`
|
||||
// and namespace `uo`, so that the seam was exercised by real content from the
|
||||
// day it was built. That prediction paid out exactly as written: the extraction
|
||||
// deleted the registration and the hook it named, and SiteHeader was not touched.
|
||||
// There is nothing for core to register now — no core nav row carries a
|
||||
// `feature` — and the filter is a correct no-op until a module supplies one.
|
||||
|
||||
// ── Extension slots (MODULE_API.md §3.7) ───────────────────────────────────
|
||||
//
|
||||
@@ -48,15 +39,22 @@ declareSlot('site.footer.status')
|
||||
// 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')
|
||||
// The invite-acceptance page's optional next step. Core owns invites — staff are
|
||||
// invited too — and owned the game-account step inside them until slice 3, which
|
||||
// meant core reading a `gameAccountSignup` flag and posting to a shard route.
|
||||
//
|
||||
// Named for the place, like the other two: it is "the point after an invite has
|
||||
// been accepted and before the invitee is sent on", not "create a game account".
|
||||
// Whether there is a step at all is the filling module's decision, made from
|
||||
// data core does not have; core renders the shell and a skip control, and hands
|
||||
// over `onDone`. With the slot unfilled the invitee goes straight to the portal,
|
||||
// which is what core's own code did whenever the flag was off.
|
||||
declareSlot('player.invite.accepted')
|
||||
|
||||
// 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)
|
||||
// Core filled the first two itself until slice 3, with the components that were
|
||||
// inline in SiteFooter.jsx and UserDetail.jsx. Both are gone: the module fills
|
||||
// all three, and core's own fills had to go for it to be able to — the first
|
||||
// fill wins, and core registered first (§3.7).
|
||||
|
||||
// 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