THROWAWAY BRANCH — evidence for the Phase 1 contract, never merged. See
modules/uo/SPIKE.md and docs/website/MODULE_API.md Part 7.
The six public spawn-atlas routes now live in modules/uo/, reached only through
the ctx/register surface, with the client half loading as a prebuilt ESM chunk.
All three exit criteria met:
• zero internal-file imports from the module into core; the built chunk has
zero bare import specifiers and bundles no React
• routes.manifest.json AND routes.guards.json are byte-identical
• /uo/atlas renders from /modules/uo/entry.js under script-src 'self' with
zero CSP violation reports
729 core tests and 81 module tests pass. Verified end to end against the real
database: the schema fragment replays after core's, onBoot runs the atlas
refresh, and the six API URLs answer unchanged.
Two things the spike changed in the contract:
• ctx.express / ctx.validator. A module lives outside server/, so Node never
reaches server/node_modules and require('express') fails outright — the
server-side twin of the one-React rule, which §2.6 had only for the client.
• window.__rg.jsxRuntime, so a module can build with the automatic JSX
runtime its tooling already assumes rather than being forced to classic.
And it confirmed §6.1 empirically: regenerating the OpenAPI spec silently
deleted all 361 lines of the atlas paths with "Swagger-autogen: Success", while
the route manifest kept all six in the same run. That is exactly the
static-analysis-vs-runtime split the fragment merge exists to prevent.
Co-Authored-By: Claude <noreply@anthropic.com>
60 lines
2.8 KiB
JavaScript
60 lines
2.8 KiB
JavaScript
// ── module-uo · server entry point ─────────────────────────────────────────
|
|
//
|
|
// SPIKE SCOPE. Phase 1 carries only /api/v1/public/atlas/* out of core
|
|
// (MODULE_SYSTEM.md §2.7): six routes, DB-backed, no sidecar, no SSE, one boot
|
|
// hook. Phase 3 brings the rest — the other 12 router/controller files, the 7
|
|
// remaining model directories, the notification-stream catalog and the
|
|
// town-crier announce leg.
|
|
//
|
|
// Called ONCE, synchronously, during the website's app.js require. Everything
|
|
// here must therefore be synchronous and must not touch the database: the route
|
|
// manifest generator and the OpenAPI generator both require app.js with the
|
|
// pool pointed at a dead port, and a module that queried here would hang both
|
|
// (MODULE_API.md §2.2). Anything needing a live database goes in onBoot.
|
|
|
|
const core = require('./core')
|
|
|
|
module.exports = function register(ctx, api) {
|
|
// Park ctx before requiring anything that reads it. The requires below pull in
|
|
// the model layer, whose files resolve core lazily — but the ORDER still
|
|
// matters for the router, which is constructed at require time.
|
|
core.init(ctx)
|
|
|
|
/* eslint-disable global-require */
|
|
const atlasRouter = require('./router/atlas.router')
|
|
const atlas = require('./model/shardAtlas/shardAtlas.model')
|
|
/* eslint-enable global-require */
|
|
|
|
const log = ctx.log('boot')
|
|
|
|
// The URL is unchanged from when this router lived in core's
|
|
// router/v1/public/index.js — that is the point, and routes.manifest.json is
|
|
// the proof (MODULE_API.md §5.3).
|
|
api.registerRoutes({
|
|
public: { '/atlas': atlasRouter },
|
|
})
|
|
|
|
// Was server.js:92, an explicit call in core's start(). Re-derive the spawn
|
|
// atlas from the shard's own ServUO tree: the shard's maps change over its
|
|
// lifetime — facets get added, replaced or renamed — so the atlas is rebuilt
|
|
// on every boot rather than shipped as a snapshot that would silently go
|
|
// stale. Hash-gated, so an unchanged tree costs one read pass and no write.
|
|
//
|
|
// Best-effort by contract: no configured path, an unreadable mount or a
|
|
// malformed file must never stop the site coming up, and a refresh that would
|
|
// REMOVE a facet is staged for admin approval instead of being applied. So it
|
|
// is caught HERE rather than left to the loader — the loader's catch would be
|
|
// correct about the failure but wrong about the severity, marking the module
|
|
// startup_failed and 503-ing six routes that serve perfectly good stale data.
|
|
api.onBoot(async () => {
|
|
try {
|
|
const result = await atlas.refreshOnBoot()
|
|
log.info('spawn atlas refreshed', { status: result && result.status })
|
|
} catch (err) {
|
|
log.warn('spawn atlas refresh failed — serving whatever was last imported', {
|
|
error: err.message,
|
|
})
|
|
}
|
|
})
|
|
}
|