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>
53 lines
2.2 KiB
JavaScript
53 lines
2.2 KiB
JavaScript
// The three helpers the town-crier leg needs from core's announce pipeline.
|
|
//
|
|
// Core owns `announce_jobs`, the worker that drains it and the retry policy;
|
|
// this module owns one leg of it (MODULE_API.md §2.4). These three lived in
|
|
// core's `announceJobs.logic` and are reproduced here rather than added to
|
|
// `ctx`, because each is a few lines of pure string handling with no state and
|
|
// no policy — the kind of thing a contract member would only make harder to
|
|
// change on both sides.
|
|
//
|
|
// The one that could NOT be vendored is `baseUrl`. Core's version reads
|
|
// `process.env.APP_BASE_URL`, and §2.7 forbids a module reading core's
|
|
// environment — it is core's deployment fact, not the module's. So it comes off
|
|
// `ctx.site.baseUrl` (API 1.1.0), read per call rather than captured, which also
|
|
// means a module built before an env change keeps agreeing with core after it.
|
|
|
|
// NOT destructured. `core.baseUrl` is a getter that resolves `ctx`, so pulling
|
|
// it out here would run at require time — before `register()` — and throw. Read
|
|
// it inside the function, where `ctx` exists.
|
|
const core = require('../core')
|
|
|
|
/** Where this deployment is reachable, without a trailing slash. */
|
|
function baseUrl() {
|
|
return core.baseUrl
|
|
}
|
|
|
|
/**
|
|
* The public link that goes in an announcement.
|
|
*
|
|
* News has no per-post route — core's SPA has only the list — so this links the
|
|
* list, matching what the pre-pipeline Discord announce did. It names a CORE
|
|
* route on purpose: the news list is core's page and stays core's through the
|
|
* whole extraction, so this is a module linking to its host, not a leftover.
|
|
*/
|
|
function articleUrl(base) {
|
|
return `${String(base || '').replace(/\/+$/, '')}/site/news`
|
|
}
|
|
|
|
/**
|
|
* Squeeze a leg client's `{ ok, status, data, error }` into the one line stored
|
|
* in `announce_job_legs.last_error` and shown in the admin panel.
|
|
*/
|
|
function legError(result) {
|
|
if (!result) return 'no response'
|
|
if (result.status) {
|
|
return result.data && result.data.message
|
|
? `${result.status}: ${result.data.message}`
|
|
: result.error || `status ${result.status}`
|
|
}
|
|
return result.error || 'request failed'
|
|
}
|
|
|
|
module.exports = { baseUrl, articleUrl, legError }
|