feat(docs): phase 14 - game-neutral core docs, an Ultima Online group, and checkTerms
All checks were successful
PR checks / checks (pull_request) Successful in 2m10s

PLAN.md §16, D63/D64, plus D67-D69 taken 2026-10-22.

- Core docs use core's words: a Team, a game server, the sidecar, the bridge
  plugin, the game module. Detail that belonged to Ultima Online moved into
  its pages rather than being dropped.
- New "Ultima Online" sidebar group (nine pages): an overview, Connect a
  ServUO shard, Verify the bridge, The shard connection, Client files,
  Troubleshooting, Bridge.cfg, sidecar.toml, Shard event catalog. Five moved
  URLs answer a real 301 (src/config/redirects.mjs, asserted by the served
  test).
- Getting started is game-neutral. "Connect your game server" keeps the old
  URL and hands off to each game; Rust links docs main:rust-link/INSTALL.md
  until phase 15. "Verify the whole stack" stays, neutral (D69).
- D68: GameExample.astro labels a game's real example on a core page with the
  game's name; checkTerms allows that game's words inside it.
- scripts/checkTerms.mjs, the twelfth check: 16 game words, docs pages and
  both sidebar trees, one allowlisted phrase (Discord's "Guild (server) ID").
  Wired into verify and pr-checks.yml.
- canonicalDocs grouped by game; the Rust group carries four documents.
- D67: the screenshot rule from #32 is adopted.

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-22 08:54:37 -05:00
parent 6046214d71
commit 4426d3e339
52 changed files with 1415 additions and 557 deletions

View File

@@ -38,6 +38,8 @@ import path from 'node:path';
import { after, before, describe, it } from 'node:test';
import { fileURLToPath } from 'node:url';
import { docsRedirects } from '../src/config/redirects.mjs';
const root = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..');
const built = fs.existsSync(path.join(root, 'dist', 'server', 'entry.mjs'));
@@ -190,4 +192,19 @@ describe('the headers the server sends', { skip: built ? false : 'no build in di
assert.equal(res.status, 403, 'the forwarded-header fix weakened the CSRF check');
});
it('sends every moved docs page to its new address with a 301 (PLAN.md §16, D60)', async () => {
const redirects = Object.entries(docsRedirects);
assert.ok(redirects.length > 0, 'src/config/redirects.mjs declares no redirects');
for (const [from, to] of redirects) {
// With and without the trailing slash: a bookmark can carry either.
for (const route of [from, `${from}/`]) {
const res = await fetch(base + route, { redirect: 'manual' });
assert.equal(res.status, 301, `${route} did not redirect`);
assert.equal(new URL(res.headers.get('location'), base).pathname, to, `${route} redirected to the wrong place`);
}
const target = await fetch(base + to);
assert.equal(target.status, 200, `${to}, the target of ${from}, does not exist`);
}
});
});