6285d937904c35f0031601ad657e7d0fcbf27e00
3
Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
82590d3ec4 |
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> |
||
|
|
045c7bf5b7 |
Read a store's config from the registry record
`GET /api/stores` records now carry a `config` field — a store's `config.json` moved into the record that already said what the store is — and the client applies it directly. Installing a store no longer depends on a second repository existing and staying reachable, and a store can be configured from the site's admin alone. The order is registry config, then a repository's `config.json`, then the engine's defaults. The middle one is why nothing has to move at once: a registry whose stores have not been migrated is read exactly as before. The window cannot supply 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 first. A config decides where files are written and, through `paths.subfolder`, which subtree the store may later delete from — not a decision the renderer gets to make, for the same reason a `GameDto` carries no paths. Tested by installing from a record carrying `subfolder: "ATTACKER"` and `install_root: "/tmp/pwned"` and finding neither on disk. A store that has left the registry, or a registry that cannot be re-read, still installs: it falls back to the engine's defaults rather than refusing. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> |
||
|
|
3d63c8a0b0 |
TypeScript, in layers, with a strict linter
The client was one main.js, one preload.js, three files in lib/ and one renderer
script. It is now a typed application whose imports point inward: domain (models,
ports, errors) knows nothing about Electron, Node or Python; application orchestrates
it through those ports; infrastructure holds the adapters — the Python CLI, HTTP, the
filesystem, Electron itself — and main, preload and renderer sit on top as hosts.
STRUCTURE.md is the map, and the deliverable as much as the code is: every layer, every
pattern in use (ports and adapters, repository vs gateway, service, DTO and mapper,
composition root, controller and router, single flight, observer streams, state store
with unidirectional flow, passive view, coded error hierarchy, frozen constant tables,
untrusted-data readers) and the naming rules — files, classes, and a verb vocabulary
for methods where find/require/read/list/apply/render/handle each state a contract.
Two properties fell out of the move, and they are why it was worth doing:
- The catalog can be driven with no window and no Electron at all. The smoke test
assembles the same services against the same ports in a plain Node process; it used
to be a script that reimplemented the bridge.
- The window never receives a filesystem path. A title crosses the bridge without
one, and launching is asked for by name, resolved in the main process from the
store's own state. Verified with a fake launcher: an unknown name answers false, a
native title resolves to its menu entry, a hosted one to its catalog URL.
Types are mandatory, including where inference would manage: explicit return,
parameter and property types, strict plus noUncheckedIndexedAccess,
exactOptionalPropertyTypes, noImplicitOverride and noPropertyAccessFromIndexSignature,
typescript-eslint strictTypeChecked and stylisticTypeChecked, exhaustive switches, no
any, no non-null assertions, and no casts on foreign data — engine stdout and the
registry go through readers that turn unknown into typed values. naming-convention
enforces the patterns rather than trusting them.
Two rule conflicts had to be decided rather than papered over. typedef and
no-inferrable-types disagree about `fallback: string = ''`: the annotation wins, since a
signature states its types. erasableSyntaxOnly is off, because it forbids constructor
parameter properties, which are how dependencies are declared here.
The preload and the renderer are bundled by esbuild into one file each: a sandboxed
preload may not require its own modules, and a module script over file:// is blocked by
the page's own origin rules. tsc compiles the rest. The package ships build/** and
package.json — 111 entries, no sources, no toolchain.
New targets: build, typecheck, lint, lint-fix, and check — typecheck, lint, then both
test suites, cheapest failure first. Every script that runs the app builds first, so a
stale bundle cannot be tested.
Nothing about the window changed: same side menu, same categories, same switcher, same
two languages. make check is clean, both test suites pass with one store and with two,
the packaged 1.3.0 bundle drives the real store, and the window was photographed before
and after — the two are the same picture.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
|