Commit Graph

4 Commits

Author SHA1 Message Date
47809854ef fix(module): ask Node whether a specifier is a builtin
All checks were successful
PR Checks / server-tests (pull_request) Successful in 12s
PR Checks / client-build (pull_request) Successful in 15s
The first CI run failed on `node:test`, in every test file, reported as the
module boundary being broken. It was not: `builtinModules` omits `test` on
Node 20 (CI) and includes it on Node 24 (local), so a list rebuilt from it
disagrees with itself across versions.

Use `isBuiltin`, which is Node's own answer, and treat the `node:` prefix as
sufficient on its own -- a prefixed specifier can never resolve to a package,
whatever the running version enumerates. Test covers both forms.

Also corrects this file's header: the client half's guard is no longer
`external` (it never worked), it is the Vite build's resolution-time check plus
checkExternals.js on the built chunk.

Co-Authored-By: Claude <noreply@anthropic.com>
2026-08-11 01:36:57 -05:00
5d7668d5ea feat(module): the bundle skeleton (phase 3, slice 0)
Some checks failed
PR Checks / server-tests (pull_request) Failing after 10s
PR Checks / client-build (pull_request) Successful in 8m45s
The first real module. It registers nothing, deliberately: what slice 0 proves
is the delivery path itself, end to end, before a single UO file moves into it.

Server half: module.json, an entry point that takes (ctx, api) and registers
nothing, a test suite built on a fake ctx, and scripts/checkImports.js -- the
MODULE_API.md §5.1 boundary check. Client half: the Vite library build, four
shims re-exporting react / react-dom/client / react-router-dom / jsx-runtime
from window.__rg, an entry that verifies each is identity-equal to core's copy,
and scripts/checkExternals.js. 29 server tests, 9 client tests, both new.

Verified against a real core: the module loads, mounts its zero routes, runs to
`started`, and is published by /api/v1/public/modules. Its chunk serves from
the entry's directory with `Cache-Control: no-cache` while the module's server
source, module.json and package.json all 404. In Chrome, under the enforced
`script-src 'self'`, the chunk evaluates and reports all four shared
dependencies OK, with zero CSP reports and no console errors.

Three findings, each of which had produced a green build that was wrong.

MODULE_API.md §3.6 shows `external` alongside the aliases and they do not
compose. Rollup asks `external` BEFORE Vite's alias resolver runs, so a
specifier in both is marked external and never aliased -- the chunk then ships
bare `import "react"`, which no browser can resolve without an import map, and
CSP forbids one. Built cleanly and emitted exactly that; checkExternals caught
it. So: alias only, `external` empty, and vite.config.js grows a resolution-time
guard that fails the build if a shared dependency resolves into node_modules.

That guard was wrong twice before it worked. Written against Rollup's `load`
hook it never ran -- `load` is first-wins and an earlier plugin had already
claimed the module -- so a deliberately-broken alias produced a 24 kB chunk with
react-router welded in, and a green build. And its forbidden-package list was
derived from the alias list "so the two cannot disagree", which meant deleting
an alias also deleted the guard against what that alias prevented. It states the
contract now, and a test asserts the aliases stay inside it.

checkImports failed on its own documentation the first time it ran: the comment
naming require("../../etc/passwd") as an example of what to catch, and index.js
explaining why the module must never require("express"). A boundary check that
cannot survive being described is one people stop writing comments around. It
strips comments and template literals with a character walk rather than a
regexp, because a URL in a string contains a comment opener and a comment
contains quotes -- and it has its own test suite, since a check never shown to
fail is a check nobody knows the state of.

Co-Authored-By: Claude <noreply@anthropic.com>
2026-08-11 01:31:37 -05:00
4a0d8f0873 ci(module-uo): gate pull requests into main on server and client checks
All checks were successful
PR Checks / server-tests (pull_request) Successful in 4s
PR Checks / client-build (pull_request) Successful in 3s
Mirrors RunicGateway/website's pr-checks.yml, since this module is two npm
packages shaped like that repo's server/ and client/ and is loaded into that
repo's process: same Node 20, same npm ci + test + build, same concurrency
cancel and the same `PR Checks / *` status pattern for branch protection.

The repo has no module code yet — the API contract is settled in Phase 1 and
Phase 3 is what extracts the UO half of website/ into here — so each half's
gates are conditional on its package.json existing. Before the code lands the
jobs report green with a notice explaining why; the moment a package.json
appears they arm themselves with no edit to this file. That is the same guard
the installer repo used through its own planning phase, and it beats both
alternatives: leaving the repo ungated, or red-Xing every governance PR.

Landing it now also gets the status contexts reported once, which is what makes
them selectable when the `main` branch-protection rule is configured.

Deliberately not here yet, because there is nothing for them to act on until
Phase 3: the release workflow that publishes module-uo-<version>.tar.gz and its
sha256, the zero-internal-imports check, and the module's own frozen route
manifest. Each lands with the code it checks.

Co-Authored-By: Claude <noreply@anthropic.com>
2026-08-10 02:40:23 -05:00
26f2079cca chore(module-uo): bootstrap repo with governance docs and templates
First commit for the UO module — the game-specific half of the Runic Gateway
website, extracted from core so that core can become game-agnostic. Phase 0 of
the module system plan (docs/website/MODULE_SYSTEM.md §2.7) calls for this repo
to get its initial commit before any module work starts.

Governance scaffolding only; no module code. The design of record settles the
API surface in Phase 1 and Phase 3 is what fills the repo, so writing module
code now would be writing against a contract that does not exist yet.

- README.md          what module-uo is, the phase table, the packaging layout,
                     and why an operator never builds anything
- CONTRIBUTING.md    planning status, the dev loop (a module is not runnable on
                     its own), the zero-internal-imports and one-path-segment
                     rules, schema fragments instead of migrations
- SECURITY.md        private reporting, plus the module-specific notes: the
                     module boundary is not a security boundary, access control
                     lives in core, and the uo-link token stays write-only
- CODE_OF_CONDUCT.md, CONTRIBUTORS.md, LICENSE.md (GPL-3.0-or-later),
  PR + issue templates — the same set every repo in the org carries
- .gitignore         Node-shaped; client/dist/ is ignored deliberately, since it
                     is a release artifact built by CI, not a source artifact

CI lands next, in a PR, mirroring how the installer repo was bootstrapped: an
empty repo cannot take a pull request, but everything after it can.

Co-Authored-By: Claude <noreply@anthropic.com>
2026-08-10 02:39:23 -05:00