Files
website/modules/uo/server/model/shardVisibility/shardVisibility.db.js
wtclaude bf470c7658 spike(modules): carry /public/atlas/* behind the proposed module surface
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>
2026-08-10 05:29:35 -05:00

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 }