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>
38 lines
1.1 KiB
JavaScript
38 lines
1.1 KiB
JavaScript
// Site-side mirror of in-game-account → website-user links. The sidecar owns the
|
|
// authoritative link (it tags the game account on /link/confirm); this model
|
|
// records it locally so the player portal can list links and enforce ownership.
|
|
|
|
const db = require('./shardLinks.db')
|
|
|
|
function toSafe(row) {
|
|
if (!row) return null
|
|
return {
|
|
account: row.account,
|
|
userId: row.user_id,
|
|
charName: row.char_name || null,
|
|
linkedAt: row.linked_at,
|
|
}
|
|
}
|
|
|
|
async function link({ account, userId, charName }) {
|
|
return toSafe(await db.upsert({ account, userId, charName }))
|
|
}
|
|
|
|
async function listForUser(userId) {
|
|
const rows = await db.listByUser(userId)
|
|
return rows.map(toSafe)
|
|
}
|
|
|
|
const ownsAccount = (account, userId) => db.isOwnedBy(account, userId)
|
|
|
|
async function getByAccount(account) {
|
|
return toSafe(await db.getByAccount(account))
|
|
}
|
|
|
|
const unlink = (account, userId) => db.remove(account, userId)
|
|
|
|
// Drop the local mirror for an account (source-of-truth severed elsewhere).
|
|
const removeByAccount = (account) => db.removeByAccount(account)
|
|
|
|
module.exports = { link, listForUser, ownsAccount, getByAccount, unlink, removeByAccount }
|