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>
45 lines
1.3 KiB
JavaScript
45 lines
1.3 KiB
JavaScript
// ── Shard feature visibility (model) ───────────────────────────────────────
|
|
//
|
|
// Thin row-shaping layer over shardVisibility.db. The policy — the ladder, the
|
|
// feature catalog, the locked fields, the kind→feature map — lives in
|
|
// utils/shardVisibility.js; this file only reads and writes rows.
|
|
|
|
const db = require('./shardVisibility.db')
|
|
|
|
// The `field_rules` JSON column comes back as a string on the mariadb driver.
|
|
function parseRules(raw) {
|
|
if (raw == null) return {}
|
|
if (typeof raw === 'object') return raw
|
|
try {
|
|
const parsed = JSON.parse(raw)
|
|
return parsed && typeof parsed === 'object' && !Array.isArray(parsed) ? parsed : {}
|
|
} catch {
|
|
return {}
|
|
}
|
|
}
|
|
|
|
const toSafe = (row) =>
|
|
row && {
|
|
feature: row.feature,
|
|
enabled: !!row.enabled,
|
|
audience: row.audience,
|
|
stream: row.stream == null ? null : !!row.stream,
|
|
fieldRules: parseRules(row.field_rules),
|
|
updatedBy: row.updated_by,
|
|
updatedAt: row.updated_at,
|
|
}
|
|
|
|
async function listAll() {
|
|
const rows = await db.listAll()
|
|
return rows.map(toSafe)
|
|
}
|
|
|
|
async function getOne(feature) {
|
|
const rows = await db.getOne(feature)
|
|
return toSafe(rows[0])
|
|
}
|
|
|
|
const upsert = (entry) => db.upsert(entry)
|
|
|
|
module.exports = { listAll, getOne, upsert }
|