feat(demo): list the demo's sign-ins on /beta and link into it per game
Some checks failed
PR checks / checks (pull_request) Failing after 2m16s

The public demo (demo.runicgateway.com) is live, so the site's demo slot
can be switched on. Two decisions of record, PLAN.md §15:

- D82: /beta reads the demo's GET /api/v1/public/settings demo.logins on
  the server, caches it for a minute, and lists the published sign-ins
  with the next wipe. If the demo does not answer, /beta links to its
  sign-in page instead. Gate two reads as in place.
- D83: a deep link into a game module's pages carries data-demo-game, and
  applyBrand.mjs fills it only for games in the new mounted demoGames
  list. The Rust features gain links into /rust and /rust/servers/demo.
  UO's stay hidden on a Rust demo.

The public-demo not-built entry is removed, because its resolvedBy was met.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01E14m6SuuY6i1vASFeGDBeY
This commit is contained in:
2026-10-28 15:32:34 -05:00
parent 8701126d64
commit 769d213c80
18 changed files with 650 additions and 63 deletions

View File

@@ -51,7 +51,7 @@
* ---------------------------------------------------------------------------------------
* `demoPath` — DEEP LINKS THAT ONLY EXIST WHEN A DEMO DOES
* ---------------------------------------------------------------------------------------
* §15/D12 keeps the public demo out of scope while requiring the site to gain one by way of
* §15/D12 kept the public demo out of scope while requiring the site to gain one by way of
* a line in a mounted `brand.json`. `brand.json`'s own comment promised `/features/` a
* "per-capability affordance" that had never been defined; D25 defines it as a deep link
* per capability that has a stable public route, appended to the mounted `demoUrl` by
@@ -63,8 +63,10 @@
* behind a Team id no static page can know. Paths are read from the real route tables —
* core's `client/src/App.jsx` and `module-uo`'s `client/src/entry.jsx` on `main` — never
* guessed. A module's public pages are namespaced under its own id, so a route into the
* Ultima Online module is `/uo/…`: the demo §15 plans runs that game, which is why the
* Teams item points there and the module-supplied items in `games/` carry their own.
* Ultima Online module is `/uo/…`. A link into a module's pages names that game
* (`demoGame` here, the `game` prop of `GameFeatures` for the lists in `games/`), and it
* shows only when the mounted `demoGames` lists it (D83): the demo that exists runs Rust,
* so the Teams item's `/uo/guilds` stays hidden there and Rust's clans link stands in.
*/
import { games } from './games/index.mjs';
@@ -82,6 +84,7 @@ import { games } from './games/index.mjs';
* @property {string[]} [caps] Module capability slugs, on a game's own items (`games/`) only.
* @property {boolean} [needsModule] Core machinery a module has to populate (D24).
* @property {string} [demoPath] A stable public route, deep-linked into a demo (D25).
* @property {string} [demoGame] The game module whose pages `demoPath` is in, if any (D83).
*
* @typedef {object} CapabilityGroup
* @property {string} id
@@ -116,6 +119,7 @@ const community = {
label: 'Teams',
needsModule: true,
demoPath: '/uo/guilds',
demoGame: 'uo',
detail:
'A roster, a leader, a private forum, its own notification streams and a Discord ' +
'voice channel, all hanging off one group. Core owns every part of that machinery ' +