A registry record is a name and a catalog
`config` — added this morning in 2.1.0 — is gone, and `storeRepositoryUrl` with it, along with the two store repositories they pointed at. 2.1.0 had the registry say how each store behaves. Wrong shape: how a store behaves is fixed per installed client, and this application is the only thing that can see the machine it runs on. A copy of that on a server was a second authority over decisions this side had already made correctly — including which directories the store may delete from — and two authorities are a way to disagree. Keeping two stores on one machine apart needs none of it. It is a subfolder, derived here: the store id is a slug of the catalog host, the home is `<id>-desktop`, the games folder is `<id>`, and that folder is the only subtree the store will ever delete from. Derived from the *catalog* on purpose — the catalog is what a store is, so two records naming the same one are the same store and land in the same place, which makes installing twice idempotent instead of a way to orphan what is already there. Existing installations keep their identity: a store home is recognised by its own `config.json`, so one installed as `ttg` stays `ttg` in `ttg-desktop` with its games where they are. Only a new install derives its id. `StoreProvisioningService` no longer re-reads the registry before installing. That existed to keep the renderer from supplying a config, and with no config in the record there is nothing left to protect: a name and a catalog have no paths in them. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -62,49 +62,34 @@ and there is nothing to decide; several and the setup screen shows a picker.
|
||||
|
||||
```json
|
||||
[
|
||||
{
|
||||
"name": "Teletype Games",
|
||||
"catalogUrl": "https://teletypegames.org",
|
||||
"storeRepositoryUrl": "https://git.teletypegames.org/stores/ttg-desktop-store",
|
||||
"config": { "store": { "id": "ttg" }, "paths": { "subfolder": "teletypegames" } }
|
||||
},
|
||||
{ "name": "Some Other Store", "catalogUrl": "https://games.example.org",
|
||||
"storeRepositoryUrl": null, "config": null }
|
||||
{ "name": "Teletype Games", "catalogUrl": "https://teletypegames.org" },
|
||||
{ "name": "Some Other Store", "catalogUrl": "https://games.example.org" }
|
||||
]
|
||||
```
|
||||
|
||||
**A store needs nothing of its own.** A name and a catalog are enough: the store
|
||||
engine's built-in defaults already cover the host-to-asset mapping, the install modes,
|
||||
the platforms and the behaviour, so what is actually missing from them is identity — a
|
||||
slug, a name and a catalog URL — and that is exactly what a registry record carries.
|
||||
With `config` and `storeRepositoryUrl` both null the client writes a three-section
|
||||
config and the store installs.
|
||||
**A name and a catalog are the whole record.** The store engine's built-in defaults
|
||||
already cover the host-to-asset mapping, the install modes, the platforms and the
|
||||
behaviour, so what is actually missing from them is identity — and identity is all a
|
||||
registry says. Nothing a record carries decides where files go: how a store behaves is
|
||||
fixed per installed client, which knows its own machine, and a copy of that on a server
|
||||
would be a second authority over decisions this side has already made.
|
||||
|
||||
Where a store's configuration comes from, in the order the client asks:
|
||||
From a record the client works out the rest:
|
||||
|
||||
1. **`config` on the registry record** — the store's own configuration, in the same
|
||||
shape a store's `config.json` had, because it is that file moved into the registry.
|
||||
It costs no request: it arrived with the store list.
|
||||
2. **`storeRepositoryUrl` → `…/raw/branch/master/config.json`** — the same thing in its
|
||||
older home, read for a registry whose stores have not moved over yet. A repository
|
||||
**without** a `config.json` is treated as no repository at all.
|
||||
3. **the engine's defaults** — when there is neither.
|
||||
- **the store id** — which names the store home and the folder games land in — is a slug
|
||||
of the catalog host (`teletypegames.org` becomes `teletypegames`), or of the display
|
||||
name if that fails. Derived from the *catalog* on purpose: the catalog is what a store
|
||||
is, so two records naming the same one are the same store and land in the same place.
|
||||
Reinstalling therefore never orphans what is already installed.
|
||||
- **the games folder** is that same slug inside the OS's usual place for programs, and it
|
||||
is the only subtree this store will ever delete from. That is the whole of how two
|
||||
stores on one machine stay out of each other's files: a subfolder, derived here.
|
||||
- **released, archived and demo** titles are listed, where the engine alone would show
|
||||
released and archived only — a catalog that publishes a demo means it to be played.
|
||||
|
||||
Two fields the record always decides, whatever the config says:
|
||||
|
||||
- **`catalogUrl` and `name`** override the config's own `store.base_url` and
|
||||
`store.name`. The registry says which catalog this store is *for*, so it wins.
|
||||
- **the store id** — which names the store home and the folder games land in — comes
|
||||
from the config's `store.id` when it sets one; otherwise from the repository name
|
||||
(`ttg-desktop-store` becomes `ttg`), then the catalog host (`teletypegames.org`
|
||||
becomes `teletypegames`), then the display name.
|
||||
|
||||
**The window never supplies a config.** It is handed stores to show and hands one back
|
||||
to install, but only as an identity: `RegistryStoreDtoMapper.toModel` drops the config,
|
||||
and `StoreProvisioningService` reads the record again from the registry before
|
||||
installing. A config decides where files are written and which subtree the store may
|
||||
later delete from, so it must not be something the renderer can set — the same rule as
|
||||
`GameDto` carrying no paths.
|
||||
Because a record has no paths in it and no config, there is nothing for the window to
|
||||
tamper with: `RegistryStoreDtoMapper.toModel` can take its choice at face value, and the
|
||||
config that lands on disk is written by the installer from the engine's own defaults.
|
||||
|
||||
What the defaults produce, for a record with no repository: the games land in a
|
||||
folder named after the store id, and released, archived **and demo** titles are
|
||||
@@ -390,6 +375,18 @@ across them. One class serving three versions is the honest way to say that.
|
||||
|
||||
## Verified, and not
|
||||
|
||||
**2.2.0** — the registry record was cut back to a name and a catalog, so the whole
|
||||
install path was measured again against a local registry serving exactly that. The slug
|
||||
came out `teletypegames` from the catalog host, the home `teletypegames-desktop`, the
|
||||
games subfolder `teletypegames`, and installing the same record twice landed in the same
|
||||
home. A record carrying `config` and `storeRepositoryUrl` — the fields a stale client or a
|
||||
tampering renderer might still send — changed nothing, because neither exists in the model
|
||||
any more. The site side was migrated and its specs re-run; the frontend was built, which
|
||||
first required removing a dead `engines` list that had been failing `vue-tsc` on master.
|
||||
|
||||
Older entries below describe what was verified for the version they name, and some of
|
||||
them predate the store engine moving into this application.
|
||||
|
||||
The pipeline's commands were run in the same containers it uses, before the pipeline was
|
||||
committed: `electronuserland/builder:22` installs, type-checks, lints, passes the smoke
|
||||
test (registry reached, store skipped as it should be on a machine that has none) and
|
||||
|
||||
Reference in New Issue
Block a user