feat(server): the whole server half (phase 3, slice 1) #3
Reference in New Issue
Block a user
No description provided.
Delete Branch "feature/module-extract-server"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Slice 1 of the Phase 3 extraction: 40 files, ~9,674 lines, 27 tables, 351 tests. Merges before its
websitecounterpart, soedgeserves these routes from core right up to the moment core drops them.This must merge before website's deletion PR, per §2.7.1's merge order.
Why it is one PR
The server half does not divide — two contract rules compose. A mount prefix is claimed whole (
/admin/shardis one 386-line router spanning atlas, clilocs, ops, visibility, market and links), and a model cannot be shared across the boundary, so it moves with the last route consuming it. Take the closure and every prefix is in it. Full reasoning in docs#135 §2.7.1. Three commits, readable in order: models+utils+schema, then routers+entry+hooks, then tests.The port mechanism
server/core.jsmakes the port a one-line import change per file rather than a signature change per function. Ported code requires its dependencies at file scope —const { query } = require('../../core')— which runs beforeregister()and before anyctxexists. So every member resolvesctxwhen called, and nothing may be destructured offctxat init either, because core is free to hand over a getter (ctx.site.baseUrlis one).Require order is load-bearing. A router does
const express = core.expressat its own file scope, socore.init(ctx)must run before the firstrequireunderrouter/. Hoisting those to the top ofindex.jsbreaks the module with an error aboutctxbeing missing, from a file that never mentions it.What was vendored, and what deliberately was not
utils/excerpt.jsis core'sderiveExcerpt— nine lines of pure text handling. Core's sanitiser sitting next to it was not copied: a second copy of a security control diverges silently the moment either is fixed.announceLinks.jsvendorslegError/articleUrlthe same way, butbaseUrlcould not be — core's readsAPP_BASE_URLand §2.7 forbids that, so it comes offctx.site.baseUrl.One deliberate behaviour change
uoLinkSocket.start()and the sidecar health probe used to run after the listener bound; they run before it now, becauseonBootdoes.start()returns as soon as the reconnecting client is armed, but the probe is a real HTTP call — so it is fired and not awaited. An unreachable sidecar must not hold the site closed: reporting the bridge is down is diagnostics, being up is not a precondition for serving a page.Verified against a running core
Loads, mounts five prefixes, replays 35 statements, warms up, reaches
started./api/v1/public/shard/statusand/atlas/metaanswer 200 with real data (800 creatures, 6,455 spawners, live economy); admin and player answer 401 from core's tier gates; the extension slot answers at/admin/users/:id/shard/*. Core's own SPA renders the shard and atlas pages unchanged against the module-served API — no console errors, no CSP reports.Zero URLs moved. Generating the manifest against core+module and diffing against the pre-extraction file: 228 before, 228 after, zero missing, zero added, and
routes.guardsidentical across all 228.One real port bug, caught by the integration run and not by tests
The atlas art map resolved
../../../db/data— correct when the file lived in core, pointing outsideserver/now. A path that happens to resolve is exactly what survives a green suite, because the absent-file branch returns{}and looks like the normal case.Needs core API 1.1.0
coreApimoves to^1.1.0:ctx.activity.log,ctx.users.getById,ctx.site.baseUrlandctx.middleware.rateLimit.wsis the module's one runtime dependency and ships inside the release tarball.The data half of the extraction: 8 model directories, 13 utils, the shard stream catalog and the 27-table schema fragment with its purge. server/core.js is what makes the port a one-line import change per file rather than a signature change per function. Ported code requires its dependencies at file scope -- `const { query } = require('../../core')` -- which runs before register() has been called and before any ctx exists. So every member is a stable function that resolves ctx when CALLED, and nothing may be destructured off ctx at init either, because core is free to hand over a getter. Two helpers are vendored rather than taken from ctx, and the line between them is the point. utils/excerpt.js is core's deriveExcerpt -- nine lines of pure text handling. Core's sanitiser next to it was NOT copied: a second copy of a security control diverges silently the moment either is fixed. announceLinks.js vendors legError and articleUrl the same way, but baseUrl could not be: core's reads APP_BASE_URL, and §2.7 forbids a module reading core's environment, so it comes off ctx.site.baseUrl. The schema fragment is core's 27 shard_*/uo_link_* statements, verbs CREATE, ALTER and UPDATE only, every CREATE TABLE guarded. Two of its tables carry a foreign key INTO users, which is allowed and is why the replay order matters -- core's schema is in place before this runs. The reverse never occurs and must not: it would make core unable to boot without a module installed. One real port bug caught by the integration run, not by tests: the atlas art map resolved `../../../db/data`, which pointed at core's tree when this file lived there and points outside server/ now. A path that happens to resolve is exactly what survives a green suite, because the absent-file branch returns {} and looks like the normal case. Co-Authored-By: Claude <noreply@anthropic.com>