Files
website/client/src/modules/version.js
wtclaude cee4ab8aea
All checks were successful
PR Checks / bot-tests (pull_request) Successful in 46s
PR Checks / server-tests (pull_request) Successful in 7m0s
PR Checks / client-build (pull_request) Successful in 7m29s
feat(demo): refuse an event step at save with demoRefuses (MODULE_API 1.19.0)
DEMO_MODE.md §3.4 refuses a denied console command when the event is
saved and when it runs. Saving is core's route, and core never asked a
module about a step's params, so the save-time half had no seam (DM18).

An event action may now declare demoRefuses(params), returning a
sentence or null. With DEMO_MODE on, create, save and publish ask it
for every step; the first sentence refuses the write 403 DEMO_LOCKED
with that sentence as the message (and in errors). A hook that throws
refuses too. Off the demo it is validated and never called, and the
catalog strips it with the other callables.

The events controller passes code and message through on create,
update and publish, so the client's one DEMO_LOCKED toast fires.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01E14m6SuuY6i1vASFeGDBeY
2026-10-26 03:39:42 -05:00

109 lines
7.7 KiB
JavaScript

// The client's copy of MODULE_API_VERSION. It must equal the server's
// (server/src/modules/version.js) — the two halves version ONE contract
// (docs/website/MODULE_API.md §1.1), and a module checks whichever half it is
// talking to: `coreApi` against the server's at load time, `window.__rg.version`
// against the client's before it registers anything.
//
// Duplicated rather than fetched, and that is deliberate. The value has to be on
// `window.__rg` before the first module chunk evaluates, which is earlier than
// any network round trip could answer — a fetched version would mean either an
// await before render or a module reading `undefined`. The cost of the copy is
// that the two files can drift, so a test asserts they agree
// (client/test/moduleRegistry.test.js) rather than trusting a bump to remember
// both.
// 1.19.0 — an event action may declare `demoRefuses(params)` (DEMO_MODE.md §3.4,
// DM18): the demo refuses a step at save. Server-side only; the refusal reaches
// the browser as an ordinary DEMO_LOCKED answer. This file bumps for the reason at the top.
// 1.18.0 — the public demo (DEMO_MODE.md §3.2): `demoLocks` in module.json,
// `ctx.demoMode`, `api.onDemoSeed`. Server-side; the client half is core's own
// banner and refusal toast, which an installed module's requests reach through
// the shared request primitive. This file bumps for the reason at the top.
// 1.17.0 — a module may take part in linking a sign-in identity (STEAM_SIGNIN.md
// §4): `ctx.identities.list`, `api.onIdentityLinking`, `api.onIdentityLinked`.
// Server-side; the client half is core's own account pages showing Steam. This
// file bumps for the reason at the top.
// 1.16.0 — a dry run's `detail.warnings` (Rust PLAN_REDESIGNS §11.4, D391): a
// module's advice, shown by core's own event editor as warning findings.
// Server-side; this file bumps for the reason at the top.
// 1.15.0 — a failed step's `retryAfterMs` (Rust PLAN_REDESIGNS §14.3.13): when
// to try again. Server-side; the run console's retry line now says when. This
// file bumps for the reason at the top.
// 1.14.0 —an action param's `multiple` (Rust PLAN_REDESIGNS §12.3): a param
// that holds several values, drawn by core's step editor as a multi-select.
// Server-side declaration; this file bumps for the reason at the top.
// 1.13.0 — the event step editor (Rust PLAN_REDESIGNS §2): event templates, a
// param's `duration`, a lease's `example` and an option's `note`. All four are
// server-side declarations the editor, core's own page, renders. This file bumps
// for the reason at the top.
// 1.12.0 — an event action's `progress()`: a live run's public page shows how
// its steps stand. Server-side registration; the client half is core's own event
// page rendering the lines. This file bumps for the reason at the top.
// 1.11.0 — `ctx.events.expired({ kind, ref })` and the ledger's `expired` status
// (Rust PLAN_FIXES D183): a module may say the game ended a ledgered resource at
// its own deadline. Server-side only; the run console, which is core's own page,
// shows the new status. This file bumps for the reason at the top.
// 1.10.0 — the event contract opens to modules (EVENTS.md §F, EVENTS_PLAN.md
// Phase 7): a module may register event actions, budget dimensions, leases and
// param option sources. All four are server-side registrations and nothing on
// `window.__rg` changed — but what they produce is met on this half, in the step
// editor: an option source is what turns a param from a text box into a dropdown
// of real values, and a budget's label and unit are what the switchboard's cap
// box says beside its number. This file bumps for the reason at the top: the two
// halves state ONE version, and a module declares one `coreApi` range against
// both.
// 1.9.0 - a module may ship its own message bodies and rules:
// `api.registerEngagementSeeds({ templates, ruleGroups })` (ENGAGEMENT.md Phase
// 11b, decision 7). Nothing on this half changed - a seed is server-side data
// and core's seeders write it on the boot path - but the bodies it ships are
// edited through the template editor this half already renders, and an operator
// meets them there. This file bumps for the reason at the top: the two halves
// state ONE version, and a module declares one `coreApi` range against both.
// 1.8.0 - the ceiling lattice gains `admin` (ENGAGEMENT.md Phase 11). Nothing on
// this half changed: a ceiling is declared on the server's `api` and enforced
// there, and the admin screens that render one read the vocabulary from
// `GET /admin/engagement/triggers` rather than holding a copy. This file bumps
// anyway, for the reason at the top - the two halves state ONE version.
// 1.7.0 — the engagement contract (docs/website/ENGAGEMENT.md Phase 2). Nothing
// on this half changed: every member the version adds is on the server's `api`
// and `ctx` (registerEventTriggers, registerAudiences, ctx.events.emit,
// ctx.inbox.push). This file bumps anyway, for the reason at the top — the two
// halves state ONE version, and a module declares one `coreApi` range against
// both. The web surfaces the engagement system needs (the rules and template
// editors, the in-app inbox) land in Phases 4, 5 and 7 and will add to this half
// then.
// 1.6.0 — the Team surface (docs/website/TEAMS.md Part 11). Nothing on this half
// changed yet: the two client additions the version covers are the `team.overview`
// and `team.member.row` slots, and a slot can only be declared by the page that
// hosts it, which lands with the Team pages in phase 3. This file bumps anyway,
// for the reason at the top — the two halves state ONE version, and a module
// declares one `coreApi` range against both.
//
// 1.5.0 — `PublicLayout` takes an optional `shell` prop ('narrow' | 'mid' |
// 'wide') that renders the `shell-… page-body` wrapper core's own pages write by
// hand. Additive: omitting it is 1.4.0's behaviour, so §3.4's "changing a kit
// component's props is major" does not bite — nothing already written changes
// meaning. It exists because the kit's acceptance run proved a module cannot
// discover the wrapper: the class names are theme.css's and appear in no
// contract, so a module page rendered outside the site's column while doing
// everything the kit said (docs/modules/kit-acceptance.md).
// 1.4.0 — a rule, not a member: §2.7 forbids a module opening a connection to a
// game server from the website process (it talks to a sidecar, which owns the
// durable copy). Nothing on window.__rg changed and nothing on the server's ctx
// changed either; this half bumps because the two halves state ONE version.
// 1.3.0 — three additions, all from Phase 3 slice 3 needing them: a nav item may
// carry an `icon` component (§3.3), core declares a third slot
// `player.invite.accepted` (§3.7), and `window.__rg.api` gained `BASE`, which
// §3.5 always documented and shared.js never published. Additive throughout: a
// module written against 1.2.0 is unaffected. The server half is untouched and
// bumps anyway, for the reason below.
// 1.2.0 — `registry` gained `registerExtension` and core gained extension slots
// (MODULE_API.md §3.7). The first change to window.__rg since 1.0.0, and an
// addition: a module that never fills a slot is unaffected. The server half is
// untouched and bumps anyway, for the reason below.
// 1.1.0 — the server's ctx gained activity.log, users.getById, site.baseUrl and
// the rate-limit factory (MODULE_API.md §2.3). Nothing on window.__rg changed,
// but the two halves state ONE version: a module declares a single coreApi range
// and is served one chunk, so a client that claimed 1.0.0 while the server
// answered 1.1.0 would be two answers to one question.
export const MODULE_API_VERSION = '1.19.0'