feat: Implemented public plugin system accessible from the store.

feat: Implemented external ryujinx integration plugin
refactor: moved sdk types and schemas to own workspace package
fix: Fixed emulator launch with no game
This commit is contained in:
Simeon Radivoev 2026-05-10 01:46:57 +03:00
parent 9051834ace
commit 38cb752552
Signed by: simeonradivoev
GPG key ID: 7611A451D2A5D37A
124 changed files with 1918 additions and 1067 deletions

View file

@ -1,59 +1,57 @@
import { ensureDir } from "fs-extra";
import { IJob, JobContext } from "../task-queue";
import { IJob, JobContext } from "@simeonradivoev/gameflow-sdk";
import { getStoreRootFolder } from "../store/services/gamesService";
import { tmpdir } from "node:os";
import path from "node:path";
import z from "zod";
import { runBunPackageCommand } from "../plugins/services";
import { PluginRegistry } from "@/shared/constants";
import path from "node:path";
import sdkPkg from '@simeonradivoev/gameflow-sdk/package.json';
export default class UpdateStoreJob implements IJob<never, never>
export default class UpdateStoreJob implements IJob<never, string>
{
static id = "update-store" as const;
static dataSchema = z.never();
packageName: string;
registry: URL;
storeVersion: string;
constructor()
{
this.packageName = process.env.STORE_PACKAGE_NAME ?? "@simeonradivoev/gameflow-store";
this.registry = new URL(process.env.STORE_REGISTRY ?? "https://registry.npmjs.org");
this.storeVersion = process.env.STORE_VERSION ?? "^0.1.0";
}
async runCommand (commands: string[])
async start (context: JobContext<UpdateStoreJob, never, string>)
{
const tempCache = path.join(tmpdir(), "gameflow-bun-cache");
const storeFolder = getStoreRootFolder();
let proc = Bun.spawn([process.execPath, ...commands, "--registry", this.registry.href, '--json'], {
cwd: storeFolder,
stdout: 'pipe',
stderr: 'pipe',
env: {
BUN_BE_BUN: "1",
BUN_INSTALL_CACHE_DIR: tempCache
}
});
let stdout = await new Response(proc.stdout).text();
console.log(stdout);
let stderr = await new Response(proc.stderr).text();
if (stderr)
console.error(stderr);
await proc.exited;
}
async start (context: JobContext<UpdateStoreJob, never, never>)
{
if (process.env.CUSTOM_STORE_PATH) return;
const storeFolder = getStoreRootFolder();
await ensureDir(storeFolder);
const storePackageFile = Bun.file(path.join(storeFolder, "package.json"));
if (!await storePackageFile.exists())
{
await storePackageFile.write(JSON.stringify({ dependencies: {} }, null, 3));
}
console.log("Adding Store Package");
await this.runCommand(["add", `${this.packageName}@${this.storeVersion}`]);
const storePackage = await Bun.file(path.join(storeFolder, "package.json")).json();
console.log("Updating Store Package");
await this.runCommand(["update", `${this.packageName}@${this.storeVersion}`]);
if (!storePackage.dependencies?.[sdkPkg.name] || storePackage.dependencies?.[sdkPkg.name] !== sdkPkg.version)
{
let response = await runBunPackageCommand(["add", `${sdkPkg.name}@${sdkPkg.version}`, "--registry", PluginRegistry, '--omit', 'peer']);
console.log(response);
}
// probably just means we couldn't find a version of the sdk, just install latest
if (storePackage.dependencies?.[sdkPkg.name] !== sdkPkg.version)
{
let response = await runBunPackageCommand(["add", '--exact', `${sdkPkg.name}@latest`, "--registry", PluginRegistry, '--omit', 'peer']);
console.log(response);
}
if (process.env.CUSTOM_STORE_PATH) return;
if (!storePackage.dependencies?.['@simeonradivoev/gameflow-store'])
{
context.setProgress(0.5, "Adding Store");
let response = await runBunPackageCommand(["add", `${this.packageName}@${this.storeVersion}`, "--registry", PluginRegistry, '--omit', 'peer']);
console.log(response);
}
}
}