Files
enricobuehler 899028e20f feat: Effect services + HttpApi implementation + kit entry/CLI/dev server
- RomConfig/RomCache on the kit's ConfigService/CacheStore; RomSync wires the
  pure domain into the kit SyncEngine (poll/watch/coalesce/fingerprint) and
  ProviderClient; EngineStatus assembly shared by REST + SSE
- makeApi: HttpApiBuilder groups over live service values (handler runtime
  shares the plugin runtime's singletons) + sseRoute status feed
- index.ts: definePluginKit entry (async-main boundary); headless-first (UI
  serve failure logged, engine continues)
- cli.ts on the kit dispatcher: scan/detect/preview offline, sync/uninstall
  online; set-password is gone with the standalone server
- dev.ts: auth-free loopback API server (:5885) — the dev:live proxy target;
  never bundled
- verified live host-less: raw config round-trip on disk, status/platforms/
  preview endpoints, soft-fail reconcile without a host
- CI: workspace install, per-package typecheck, publish from plugin/

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-20 19:04:25 +02:00

110 lines
3.6 KiB
TypeScript

// Domain DTOs shared by the plugin server and the UI — Schemas are the source of truth,
// the derived types keep the original domain names so the pure core reads unchanged.
import { Schema } from "effect";
/** Host OS as the launch/quoting domain sees it (macOS hosts use the linux lane). */
export const Os = Schema.Literals(["linux", "windows"]);
export type Os = typeof Os.Type;
/** Default emulator + core for a platform (operator-overridable per platform / per game). */
export const DefaultLaunch = Schema.Struct({
emulator: Schema.String,
core: Schema.optionalKey(Schema.String),
extraArgs: Schema.optionalKey(Schema.String),
});
export type DefaultLaunch = typeof DefaultLaunch.Type;
/** A console platform: extension set, art key, default launch (design §5). */
export const Platform = Schema.Struct({
id: Schema.String,
name: Schema.String,
extensions: Schema.Array(Schema.String),
libretroSystem: Schema.optionalKey(Schema.String),
defaultLaunch: DefaultLaunch,
disc: Schema.optionalKey(Schema.Boolean),
});
export type Platform = typeof Platform.Type;
/** An emulator definition: per-OS detection, launch template, archive support. */
export const EmulatorDef = Schema.Struct({
id: Schema.String,
name: Schema.String,
detect: Schema.Struct({
linux: Schema.Array(Schema.String),
windows: Schema.Array(Schema.String),
}),
coresDir: Schema.optionalKey(
Schema.Struct({
linux: Schema.Array(Schema.String),
windows: Schema.Array(Schema.String),
}),
),
template: Schema.String,
supportsArchives: Schema.Boolean,
contested: Schema.optionalKey(Schema.Boolean),
});
export type EmulatorDef = typeof EmulatorDef.Type;
/** A detected emulator install (UI's Emulators page + launch resolution). */
export const DetectedEmulator = Schema.Struct({
id: Schema.String,
name: Schema.String,
template: Schema.String,
supportsArchives: Schema.Boolean,
contested: Schema.optional(Schema.Boolean),
exeToken: Schema.String,
exePath: Schema.optional(Schema.String),
appId: Schema.optional(Schema.String),
via: Schema.Literals(["path", "file", "flatpak"]),
coresDir: Schema.optional(Schema.String),
cores: Schema.optional(Schema.Array(Schema.String)),
});
export type DetectedEmulator = typeof DetectedEmulator.Type;
/** A candidate the compute rejected, with a human reason (Library page, CLI). */
export const Skipped = Schema.Struct({
external_id: Schema.String,
title: Schema.String,
reason: Schema.String,
});
export type Skipped = typeof Skipped.Type;
/** A summary of one desired-state compute (Overview + Library pages). */
export const SyncReport = Schema.Struct({
considered: Schema.Number,
included: Schema.Number,
skipped: Schema.Array(Skipped),
excluded: Schema.Array(
Schema.Struct({ external_id: Schema.String, title: Schema.String }),
),
warnings: Schema.Array(Schema.String),
truncated: Schema.Number,
perPlatform: Schema.Record(Schema.String, Schema.Number),
overWarn: Schema.Boolean,
});
export type SyncReport = typeof SyncReport.Type;
export const LastSync = Schema.Struct({
fingerprint: Schema.String,
count: Schema.Number,
at: Schema.Number,
});
export type LastSync = typeof LastSync.Type;
/** The engine status the Overview page renders and the SSE feed streams. */
export const EngineStatus = Schema.Struct({
rootsConfigured: Schema.Number,
os: Os,
artProvider: Schema.NullOr(Schema.String),
syncing: Schema.Boolean,
lastSync: Schema.optional(LastSync),
lastReport: Schema.optional(SyncReport),
detectedAt: Schema.optional(Schema.Number),
paths: Schema.Struct({
dir: Schema.String,
config: Schema.String,
cache: Schema.String,
}),
});
export type EngineStatus = typeof EngineStatus.Type;