feat(cliloc): import the table from the shard, not from a file someone converted (Phase 2)
The base cliloc table now comes over the bridge. `clilocBridge.js` walks
`GET /cliloc` page by page and the model merges the `custom/` overlays over it —
overlays stay on disk because ServUO has no server-side notion of a custom
cliloc, so there is nothing on the shard to ask for.
**The shard wins whenever uo-link is configured and enabled**, with no mode
setting: there is no version of "which source?" an operator benefits from
answering. A file on disk remains the source only where there is no shard link,
plus a one-off explicit `path` — deprecated, not removed, and unchanged.
**Boot no longer imports on the bridge.** The file path could hash 5 MB locally
and skip in 14 ms; a shard round trip in the boot sequence would be spent
answering "no" on every restart but the one after a client patch — and patching a
client is an operator action, so importing became one. Admin → Shard → Import.
Whatever table is loaded keeps serving until then.
Three checks in the walk, each for a way a shard can hand back a table that looks
complete:
* only `cut: 'end'` finishes it — a short page can equally be a spent budget,
and a truncated table renders some items named and some not, which is exactly
what NO table looks like;
* the cursor must advance, or the walk stops rather than spinning;
* every page echoes the source's size and mtime, so a client patched mid-import
is refused outright rather than stitched from two files.
**The base is exempt from the vanished-source rule**, which is an upgrade detail
rather than a preference: an install that used the file pipeline carries its base
file's label in the stored fingerprint, and on the bridge that label is *supposed*
to disappear. Counting it as vanished would demand an approval for a change the
upgrade itself made. Overlays keep the rule in full.
**The protocol pin moves 7 → 8** — the third declaration site, and the one
nothing enforces. Phase 1 moved the sidecar and the overlay together because the
installer refuses a mismatched bundle; this one has to be moved by hand, in the
phase that first calls a protocol-8 route. The schema block above it is the
record of what forgetting costs: two phases of every REST call answered 409.
Verified against a live shard, sidecar and site: 12 pages, 67,496 rows imported
in 1.68 s, the operator's three-row overlay overriding stock strings on top of
it, and the next import correctly `unchanged`.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016wDDVXWMDz82WqE1i969r4
This commit is contained in:
@@ -178,6 +178,37 @@ const getPointsBoard = (system) => call(`/points/${encodeURIComponent(system)}`)
|
||||
const getMarket = ({ limit = 200, offset = 0 } = {}) =>
|
||||
call(`/market?limit=${encodeURIComponent(limit)}&offset=${encodeURIComponent(offset)}`)
|
||||
|
||||
// ── Protocol 8: the Asset Bridge (docs/link/v8.md) ────────────────────────
|
||||
//
|
||||
// The shard reads the operator's own UO client files and hands the results over
|
||||
// this link, which is why nobody has to install UOFiddler any more.
|
||||
|
||||
// Stage 1 of the import gate: what those client files currently ARE — size, mtime
|
||||
// and content hash of each, plus the version of the shard's extractor that would
|
||||
// read them. No pixels and no strings cross on this call; its whole job is to let
|
||||
// the site decide that nothing has changed and stop, which is the normal case on
|
||||
// every restart.
|
||||
//
|
||||
// `sha256` comes back NULL for a file the shard has not hashed yet (anim.mul is
|
||||
// 195 MB and hashing it cannot fit in a reply), with `hashing: true` alongside.
|
||||
// That is "ask again in a moment", not "the file changed".
|
||||
const getAssetSources = () => call('/assets/sources')
|
||||
|
||||
// The cliloc table out of the shard's own client, PAGED: each reply carries `rows`
|
||||
// plus `more` / `cursor` / `cut`, and the caller echoes the cursor back until a
|
||||
// reply says `more: false`. Only `cut: 'end'` means the table is finished — a short
|
||||
// page can equally mean the byte budget was spent.
|
||||
//
|
||||
// `clilocBridge.js` is the thing that walks it; nothing else should call this
|
||||
// directly, because a half-walked table is worse than none.
|
||||
const getClilocTable = ({ lang, cursor } = {}) => {
|
||||
const params = new URLSearchParams()
|
||||
if (lang) params.set('lang', lang)
|
||||
if (cursor) params.set('cursor', cursor)
|
||||
const qs = params.toString()
|
||||
return call(`/cliloc${qs ? `?${qs}` : ''}`)
|
||||
}
|
||||
|
||||
// ── Commands ──────────────────────────────────────────────────────────────
|
||||
const confirmLink = (code, websiteUserId) =>
|
||||
call('/link/confirm', { method: 'POST', body: { code, websiteUserId: String(websiteUserId) } })
|
||||
@@ -398,6 +429,8 @@ module.exports = {
|
||||
getPoints,
|
||||
getPointsBoard,
|
||||
getMarket,
|
||||
getAssetSources,
|
||||
getClilocTable,
|
||||
confirmLink,
|
||||
linkLookup,
|
||||
createAccount,
|
||||
|
||||
Reference in New Issue
Block a user