A WarpEngine Client egy WarpEngine-áruház asztali alkalmazása: a katalógus kártyarácsként, egy kattintás telepít egy címet az alkalmazásmenüdbe, egy elindítja, egy eltávolítja. Linux, macOS és Windows.

Az áruházmotor az alkalmazás része. A katalógus beolvasása, a géphez illő kiadás kiválasztása, a kicsomagolás, a menübejegyzés kiírása és annak nyilvántartása, hogy mi hova került, mind az alkalmazáson belül történik. Semmit nem kell előbb telepíteni, nem kell értelmezőt keresni.
Az, hogy melyik áruházból telepít, nincs belefordítva: a kliens megkérdez egy nyilvántartást (GET /api/stores az oldalon), és egy ottani rekord egy név és egy katalógus, semmi több. A viselkedés a kliens saját dolga, a játékmappa pedig a katalógushoszt slugja, így két áruház egy gépen soha nem nyúl egymás fájljaihoz.
python3, python és py -3 parancsokat, és világosan megmondja, ha egyik sem válaszol.Vedd le a gépedhez való csomagot a kiadások közül, és nyisd meg. Az első indításkor, ha nincs áruház a gépen, az ablak felajánlja, hogy letölt egyet. Ennyi az egész beállítás.
A build ad hoc alá van írva, de nincs notarizálva, ezért a macOS rákérdez, mielőtt egy böngészőből érkezett példányt futtatna:
xattr -dr com.apple.quarantine "/Applications/WarpEngine Client.app"
A Rendszerbeállítások ▸ Adatvédelem és biztonság alatti Megnyitás mindenképp is működik, egy blokkolt próbálkozás után. Ezt a lépést csak a notarizálás szünteti meg, ahhoz pedig fizetős Apple Developer ID kell. Az áruház által később letöltött fájlokat ez nem érinti: a Python nem állítja be a karanténjelzőt.
A v1.0.0-t egyáltalán nem lehetett megnyitni — azt jelentette, hogy „is damaged". A csomag soha nem lett aláírva; csak a fő futtatható állományán volt rajta a linker ad hoc aláírása, tehát nem volt erőforrás-pecsét, és a Gatekeeper kérdés helyett elutasította. A scripts/after-pack.js most build közben aláírja a csomagot, az eredmény pedig valid on disk státusszal ellenőrizhető.
Ebben a kliensben semmi nem tud semmit egyetlen konkrét áruházról sem. Az ár, hogy kell-e fiók, hol lehet megvenni, hol lehet belépni — mindez a katalógus szerverétől jön: a WarpEngine 0.5 a GET /api/service végponton megmondja, mit kínál, és minden bejegyzésre rátesz egy access blokkot. Ezért szolgál ki egyetlen build bármilyen boltot.
Ahol a szerver nem kínál belépést (minden 0.5 előtti WarpEngine, és minden olyan áruház, amely nem árul semmit), ott az ablak sem mutat ilyet. Ahol igen:
safeStorage API-ján keresztül, áruházanként egy. Ha nincs kulcstartó, akkor egyáltalán nem tárolja el, nem pedig nyíltan kiírja; ennek az ára az, hogy a következő indításnál újra be kell lépni.Egy kártyán az, hogy egy ember mit tehet, elválik attól, hogy a gép mit tud futtatni:
| Állapot | Mit kínál a kártya |
|---|---|
| birtokolt, vagy ingyenes | Telepítés |
| nem birtokolt | az ár, és a Megveszem, amely böngészőben nyitja meg az áruházi oldalt |
| kijelentkezve, a katalógus kapuzza | Belépés a telepítéshez — a katalógus addig nem tudja megmondani, kié, amíg nem tudja, ki kérdez |
A vásárlás a böngészőben történik: egy itt újraépített pénztár csak egy második hely lenne, ahol el lehet rontani a kártyakezelést. A Frissítés utána Telepítéssé alakítja a kártyát.
Ezzel jön két kategória is — Birtokolt és Megveendő —, mert a birtoklás nem ugyanaz, mint a telepítettség.
Egy meg nem vett cím nincs kiszürkítve. A kiszürkítés azt jelenti, hogy a gép nem tudja futtatni; a géppel semmi baj, ha egy cím egyszerűen pénzbe kerül.
Bearer token soha nem megy el olyan hosztra, amely nem adta ki. Egy kapuzott letöltés aláírt tárolóra irányít át — gyakran valaki más szerverére —, és egyes objektumtárolók visszautasítják azt a kérést, amely Authorization fejlécet és aláírást is visz.
Az első indításkor a kliens lekéri a nyilvántartást. Egy áruház esetén nincs mit eldönteni; több esetén a beállítóképernyő választót mutat.
[
{ "name": "Teletype Games", "catalogUrl": "https://teletypegames.org" },
{ "name": "Some Other Store", "catalogUrl": "https://games.example.org" }
]
Egy név és egy katalógus a teljes rekord. A kliens már tudja a hoszt–asset leképezést, a telepítési módokat, a platformokat és a viselkedést. Amit nem tudhat, az az identitás, és egy nyilvántartás csak az identitást mondja meg. Egy rekordból ezeket származtatja:
teletypegames.org → teletypegames), vagy a megjelenített névé, ha az nem megy. Ez adja az áruház könyvtárának és a játékmappának a nevét. Két rekord, amely ugyanarra a katalógusra mutat, ugyanaz az áruház, és ugyanoda kerül, így az újratelepítés soha nem hagy árvát.A nyilvántartás címe az utolsó oldalspecifikus dolog a kliensben, három helyen dönthető el, a legspecifikusabbal kezdve: egy futásidejű STORES_API, az a cím, amellyel a csomag épült, majd a miénk.
STORES_API=http://127.0.0.1:8731/stores npm start # kipróbálás
make dist STORES_API=https://games.example.org/api/stores # kiadás
A buildváltozat a becsomagolt alkalmazás package.json fájljába kerül (warpEngine.registryUrl), így egy más katalógusra épített kliens nem igényel forrásmódosítást, és a felhasználó gépén sem kell semmit beállítani. Egy áruház hozzáadása adatbázissor az oldalon, nem ennek az alkalmazásnak a kiadása.
Minden, ami nem cím, az oldalmenüben él; a menügomb becsukja, és az állapot túléli az újraindítást.
| Szakasz | Mit tartalmaz |
|---|---|
| Áruházak | a gépen lévő összes áruház, a nyitottat megjelölve. Egy másikra kattintva vált a rács, a kategóriák és a mappák, és a kliens legközelebb ott nyílik meg. Ugyanabból a katalógusból származó, de más mappában lévő két áruház az azonosítója helyett a mappáját mutatja. Az Áruház hozzáadása megnyitja a nyilvántartás-választót. |
| Fiók | Belépés… / Kilépés, csak ott, ahol a katalógus kínál belépést. |
| Műveletek | Frissítés, amely újraolvassa a katalógust. A címek egyesével, a kártyájukról telepíthetők; nincs „telepíts mindent" gomb. |
| Kategóriák | egyszerre egy, darabszámokkal: Minden, Telepített, Frissítések, Nincs telepítve, majd egy-egy sor platformonként és típusonként (natív vagy hosztolt). Abból épül, amit a katalógus tartalmaz, tehát az üres platform nem jelenik meg, és az eltűnő kategória visszaesik a Minden értékre. Egy WarpEngine-katalógusban nincs műfajmező. |
| Napló | az áruház saját kimenete szó szerint, plusz az a két mappa, ahova minden kerül. Kérésig rejtve. |
| Nyelv | a rendszert követi, átváltható: angol és magyar. |
Egy kártya gombja Telepítés, Frissítés, vagy telepítés után Játék / Megnyitás. Az Eltávolítás visszaveszi a címet. Minden kártya megmondja, hogy natív-e — kicsomagolva, helyben futtatva, offline is működik —, vagy hosztolt: a katalógus által kiszolgált böngészős build, amelynek a bejegyzése oldalt nyit meg, és hálózatot igényel.
A katalógus teljes tartalma megjelenik, azt is beleértve, amit ez a gép nem tud telepíteni. Ezek a kártyák ki vannak szürkítve, nem támogatott platform vagy ehhez a géphez nincs build jelvényt viselnek a motor magyarázatával, és nincs rajtuk mit megnyomni — mondjuk egy C64-kazetta egy asztali gépen. Az az áruház, amely elrejti őket, találgatni hagyja a látogatót, hogy a katalógus kicsi-e, vagy a gépe fura. Saját kategóriájuk van, a Nem ehhez a géphez, és kimaradnak a natív/hosztolt darabszámokból, mert az a cím, amelyhez nincs build, nem tartozik egyik módhoz sem. Ehhez asztali áruházmotor 1.2.0 kell warpstore 1.4.0-n; régebbi motorral a lista mindenestül telepíthető, mint korábban.
Minden kártya azonos magasságú borítóképsávot visel — kép híján a cím első betűjét —, hogy a gombok soron belül egy vonalba essenek. Ez csendben el volt rontva addig, amíg le nem fényképeztük az ablakot: a sorok a tartalmuk követése helyett egyenlően osztották el az ablakmagasságot, ami összenyomta a képet és levágta a gombokat, miközben minden automatikus darabszám-ellenőrzés átment, mert a csomópontok mind ott voltak az oldalon.
Amíg az áruház dolgozik, csak azok a dolgok vannak letiltva, amelyek egy második hívást indítanának. A menü, a naplófiók és a szűrők tovább működnek, mert a képernyőt változtatják, a lemezen semmit.
Bármi, ami itt települ, normál menübejegyzés, tehát az indítódban, a Dockban vagy a Start menüben is megjelenik. A játékhoz nem kell futnia az alkalmazásnak.
A make a bejárati ajtó; az npm-szkripteket csomagolja be. A make önmagában mindent kilistáz.
| Cél | Mit csinál |
|---|---|
make setup |
függőségek telepítése (előbb ellenőrzi a Node verzióját) |
make build |
TypeScript fordítása, a preload és a renderer bundle-ölése |
make typecheck |
típusellenőrzés, kimenet nélkül |
make lint |
a szigorú szabálykészlet (a make lint-fix javítja, amit tud) |
make check |
típusellenőrzés, lintelés és mindkét tesztkészlet — ez a kapu |
make start |
futtatás a telepített áruház ellen |
make smoke |
az áruházhíd meghajtása ablak nélkül |
make uitest |
az ablak egyszeri betöltése, és jelentés arról, mi rajzolódott ki |
make test |
mindkét ellenőrzés |
make dist |
csomagolás ehhez a géphez (dist-mac, dist-win, dist-linux) |
make publish |
a dist/ alatti csomagok feltöltése a Gitea-kiadásra |
make release |
csomagolás és publikálás egy menetben |
make clean |
a megépített csomagok törlése (a distclean a node_modules mappát is elhagyja) |
make version |
az érintett verziók, azt is beleértve, hogy megvan-e a tea |
Az npm-szkriptek továbbra is működnek közvetlenül (npm start, npm run dist:mac). A Makefile a kiadási lépésen kívül nem tesz hozzá logikát.
A .woodpecker.yaml ebben a repóban van, nem az update szerver konfigurációs kiterjesztésében: az a kiterjesztés játékplatform-pipeline-okat szolgál ki, amelyek kazettát építenek és az oldal katalógusába publikálnak, míg ez alkalmazást épít, és egy kiadásba publikál.
| Lépés | Image | Mit csinál |
|---|---|---|
check |
electronuserland/builder:22 |
telepítés, típusellenőrzés, lintelés, füstpróba |
linux |
electronuserland/builder:22 |
AppImage és deb |
windows |
electronuserland/builder:22-wine |
NSIS-telepítő és hordozható exe, Wine-on keresztül |
release |
alpine |
csak tagre: létrehozza a kiadást, és hozzácsatolja, amit épített |
A macOS helyi build marad — az Apple eszközlánca csak Macen létezik —, így egy kiadásnak két fele van: egy vX.Y.Z tag pusholása felépíti a Linuxot és a Windowst, és létrehozza a kiadást, a Macen futtatott make release pedig felteszi a macOS-csomagot ugyanarra a kiadásra. Bármelyik fél mehet előbb. Az ablakteszt is helyi: kijelző és a gépen lévő áruház kell hozzá.
A publikáláshoz gitea_token repótitok kell írási joggal. A Woodpecker ad a lépéseknek forge-hitelesítő adatot, és a szkript ezt használja, ha a titok hiányzik, de nem lehet rá támaszkodni: egy kézi buildnek megvan, a tag-webhook által indítottnak nincs. Két publikáló létezik — a scripts/release.sh a tea bejelentkezést vezérli egy munkaállomáson, a scripts/ci-upload.sh pedig az API-val beszél azzal a hitelesítő adattal, ami a CI-nak van.
A windowsos telepítő nincs aláírva: a Windows ismeretlen kiadóra figyelmeztet, amíg nincs kódaláíró tanúsítvány. A Linux-csomagok konvenció szerint nem visznek aláírást.
make release
make publish TAG=v1.0.2 # a package.json-tól eltérő tag
scripts/release.sh dist/one-file.dmg # csak egy csomag
A tag a package.json fájlból jön, tehát az npm version patch az egyetlen hely, ahol verziót állítunk. A kiadás létrejön, ha nincs, és az a melléklet, amelynek a neve már szerepel, lecserélődik, így egy újraépítés plusz egy második make publish felkerül, nem hibázik. A kiadási jegyzetek a RELEASE_NOTES.md fájlból jönnek, ha van. A repót az origin alapján olvassa, tehát egy fork a forkba publikál.
Telepített és bejelentkezett tea kell hozzá — a devarea repóban van make tea. Felülbírálható: TAG, REPO, TEA_LOGIN, NOTES, DIST.
A telepítéshez Node 22+ kell, a futtatáshoz nem: az Electron telepítője csak ESM, a régebbi Node pedig nem tudja require()-elni. A becsomagolt alkalmazás a saját futtatókörnyezetét viszi.
Az npm run uitest saját felhasználóiadat-könyvtárat használ, és kihagyja az egypéldány-zárat. Különben a felhasználónál már nyitva lévő példány elnyelné a tesztfolyamatot, amely 0-val lép ki, és sikeresnek látszik.
Mindkét tesztszkript elfogad egy homokozó-áruházat, így lehet ezt a repót anélkül tesztelni, hogy egy működő telepítéshez hozzányúlnánk:
STORE_ROOT=/tmp/sandbox-root npm start
SMOKE_HOME=/tmp/sandbox-root/ttg-desktop npm run smoke
TypeScript rétegekben, a függőségek befelé mutatnak. A repóban lévő STRUCTURE.md a térkép — rétegek, minták, elnevezési szabályok —, és ez az a fájl, amit el kell olvasni, mielőtt bármit hozzáadnál.
| Réteg | Mi él ott |
|---|---|
src/shared/ |
az IPC-csatornatábla, a hídszerződés, a DTO-k, az üzenetkészletek |
src/domain/ |
modellek, portok és hibák — se Electron, se Node, se Python |
src/application/ |
szolgáltatások és a domain–DTO leképezők |
src/infrastructure/ |
adapterek: a Python-parancssor, a HTTP, a fájlrendszer, az Electron |
src/main/ |
az ablak, az IPC-kontrollerek, a kompozíciós gyökér, az önteszt |
src/preload/ |
a híd, egyetlen fájlba bundle-ölve — egy homokozott preload nem tud modult behúzni |
src/renderer/ |
az állapottár, a nézetek és a renderer-kontrollerek |
src/scripts/ |
a füstpróba: ugyanazok a szolgáltatások ablak nélkül |
Két tulajdonság, amit a rétegek megvesznek:
A linter szándékosan szigorú: explicit típusok minden szignatúrán, nincs any, nincs non-null assertion, kimerítő switchek, a kívülről jövő adat típuscastolás helyett ellenőrzött olvasókon át. Az a fordítás, amelyből hiányzik egy kulcs, nem fordul le, és az a csatorna sem, amelyet a preload nem valósít meg.
| Fájl | Mit csinál |
|---|---|
main.js |
az ablak, az IPC, az egyszerre-egy-hívás őr |
preload.js |
a teljes felület, amit a renderer megkap — Node nem jut el hozzá |
lib/store.js |
megkeresi az áruházat és a Pythont, futtatja a parancssort, feldolgozza a JSON-t |
lib/bootstrap.js |
beolvassa a nyilvántartást, letölti a motort, a magot és egy konfigurációt |
lib/i18n.js |
a két szövegtábla |
renderer/ |
sima HTML, CSS és JS — nincs keretrendszer, nincs buildlépés |
scripts/release.sh |
létrehozza a Gitea-kiadást, és lecseréli a mellékleteit |
scripts/after-pack.js |
csomagolás közben ad hoc aláírja a macOS-csomagot |
contextIsolation be, nodeIntegration ki, sandbox be, és olyan CSP, amely csak az alkalmazás saját szkriptjét és stíluslapját, valamint HTTPS-en érkező képeket enged. A linkek a valódi böngészőben nyílnak; az ablak soha nem navigál.
A lib/store.js a --json kapcsolón keresztül beszél a parancssorral: adat a stdouton, az emberi napló a stderren. Ez a kapcsoló az 1.1.0-s motorverzióval érkezett, és a kliens ellenőrzi is — egy régebbi áruházat elbukott hívás helyett frissítési ajánlat fogad.
A híd egy ENGINES listát tart, ma egyetlen bejegyzéssel. A RetroArch-áruház parancsalakja ugyanez, tehát egy második bejegyzés a teljes változtatás ahhoz, hogy azt is meghajtsa.
macOS-en (arm64) végigpróbálva a kiadás becsomagolt alkalmazásával, nem fejlesztői futással: az áruházat megtalálja, a katalógus kilistázódik, egy szinkron telepít, az ablak kirajzolja a telepített állapotot, és az npm run uitest átmegy kirajzolt ráccsal és mindkét nyelvvel a választóban. A bootstrap-letöltést üres könyvtárba futtattuk, és a keletkezett áruház válaszolt a hídnak.
SELFTEST_SHOT beállításával az ablak PNG-be fényképezi magát, így találtuk meg az összenyomott sorokat, és így igazoltuk a javítást — mindkét nyelven, nyitott és csukott menüvel.config.json fájllal rendelkező és egy anélküli áruházrepóval. Mindkettő települt, és a motor mind a tíz címet kilistázta a szintetizált konfigurációval. Az uitest lefutott áruházzal (rács), áruház nélkül (beállítókapu, mindkét név a választóban), és elérhetetlen nyilvántartással (újrapróbálási kapu).codesign --verify --deep --strict tiszta, a syspolicy_check pedig csak a várt „adhoc signed" figyelmeztetést jelentette. Egy karanténos példányt jóváhagyásig továbbra is megállít — ez Gatekeeper-szabályzat, nem a csomag hibája.Linuxon és Windowson nem próbáltuk ki. Az útvonalak és az indítási viselkedés meg vannak írva rájuk, és az áruház parancssorában ugyanez a hiányosság: .desktop és .lnk indítók generálódtak és olvashatók, de még senki nem kattintott rájuk.