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>
38 lines
1.4 KiB
JavaScript
38 lines
1.4 KiB
JavaScript
const { query } = require('../../core')
|
|
|
|
// One row per shard feature. Absent rows are fine — utils/shardVisibility.js
|
|
// compiles a default for every known feature and merges stored rows over it, so
|
|
// a fresh install with an empty table behaves exactly as the site did pre-v3.
|
|
|
|
const COLS = 'feature, enabled, audience, stream, field_rules, updated_by, updated_at'
|
|
|
|
const listAll = () => query(`SELECT ${COLS} FROM shard_feature_visibility`)
|
|
|
|
const getOne = (feature) =>
|
|
query(`SELECT ${COLS} FROM shard_feature_visibility WHERE feature = ?`, [feature])
|
|
|
|
// Upsert one feature's settings. `fieldRules` is stored as a JSON object of
|
|
// {field: rung}; the caller has already stripped locked fields and validated
|
|
// every rung against the ladder.
|
|
const upsert = ({ feature, enabled, audience, stream, fieldRules, updatedBy }) =>
|
|
query(
|
|
`INSERT INTO shard_feature_visibility (feature, enabled, audience, stream, field_rules, updated_by)
|
|
VALUES (?, ?, ?, ?, ?, ?)
|
|
ON DUPLICATE KEY UPDATE
|
|
enabled = VALUES(enabled),
|
|
audience = VALUES(audience),
|
|
stream = VALUES(stream),
|
|
field_rules = VALUES(field_rules),
|
|
updated_by = VALUES(updated_by)`,
|
|
[
|
|
feature,
|
|
enabled ? 1 : 0,
|
|
audience,
|
|
stream ? 1 : 0,
|
|
fieldRules == null ? null : JSON.stringify(fieldRules),
|
|
updatedBy ?? null,
|
|
],
|
|
)
|
|
|
|
module.exports = { listAll, getOne, upsert }
|