Files
Wand-Enhancer/web-panel/bridge/scripts/default/installed-apps-sync/services.js
T
kitbyte 13759b1db6 feat: ship 1.0.8.0 release automation and runtime overhaul
- reduce ASAR IO overhead with streamed archive reads, buffered copies, faster relative-path handling and placeholder integrity records
- fix in-place app.asar.unpacked packing/extraction self-copy cases that caused locked-file failures
- tighten JS patch discovery with candidate bundle filters and search hints
- require prebuilt remote-panel dist artifacts and clean up embedded bridge/script packaging
- add unified build entrypoints for PowerShell, cmd and bash and move native CMake output under .tmp
- add release metadata validation, changelog section extraction, pre-commit hook and GitHub Actions validation/release pipelines
- make CHANGELOG the source of truth for release notes and document the tag-driven release flow
- add updater release notes UI with latest/full changelog loading and localize the new update strings
- modularize bridge renderer scripts, add installed apps and game status sync, and support remote launch/stop commands
- centralize bridge protocol, IPC and WebSocket constants and improve LAN IP selection for QR pairing
- refactor remote panel controls/state enums, persist accent color, polish library/session UI and refresh assets
2026-05-06 13:07:09 +03:00

255 lines
6.8 KiB
JavaScript

import { TRAINER_LAUNCH_REQUEST_EXPORT_KEY } from "./constants.js"
import {
findExportedConstructor,
findInstanceInContainerGraph,
isRecord,
} from "./runtime.js"
export function hasMissingOptionalServices(state) {
return (
!state.gameLifecycleService ||
!state.trainerVisibilityService ||
!state.unavailableTitlesService
)
}
const OPTIONAL_SERVICE_SPECS = [
{
stateKey: "unavailableTitlesService",
methods: ["getUnavailableTitle", "searchUnavailableTitles", "getUnavailableTitlesByCorrelationIds"],
label: "Unavailable titles service",
},
{
stateKey: "gameLifecycleService",
methods: ["onGameLaunched", "onGameEnded", "launch"],
label: "Game lifecycle service",
},
{
stateKey: "trainerVisibilityService",
methods: ["onDisplayTrainerChanged", "onVisibleTrainerChanged", "onRunningTrainerChanged"],
label: "Trainer visibility service",
},
{
stateKey: "trainerApiService",
methods: ["getLatestLocalTrainerForGame", "getMostCompatibleTrainerForGame", "getTrainerById"],
label: "Trainer API service",
},
{
stateKey: "trainerService",
methods: ["launch", "endTrainer", "onNewTrainer", "onTrainerEnded"],
label: "Trainer service",
},
]
export function resolveOptionalServices(state, container, webpackRequire) {
for (const spec of OPTIONAL_SERVICE_SPECS) {
if (!state[spec.stateKey]) {
state[spec.stateKey] = resolveOptionalService(state, container, webpackRequire, spec)
}
}
if (!state.trainerLaunchRequestCtor) {
state.trainerLaunchRequestCtor = getTrainerLaunchRequestCtor(state, webpackRequire)
}
}
export function getInstalledAppsService(state, container, webpackRequire) {
const ctor = findExportedConstructor(webpackRequire, (prototype) => {
return (
typeof prototype.refreshApps === "function" &&
typeof prototype.watchGame === "function"
)
})
if (!ctor) {
state.log(
"warn",
"Installed apps service constructor not found in webpack cache."
)
return null
}
try {
const service = container.get(ctor)
const appsCount = isRecord(service.installedApps)
? Object.keys(service.installedApps).length
: -1
const catalogGamesCount = isRecord(service.catalog?.games)
? Object.keys(service.catalog.games).length
: -1
state.log(
"info",
"Installed apps service resolved.",
`ctor=${ctor.name || "<anon>"}, installedApps=${appsCount}, catalogGames=${catalogGamesCount}`
)
return service
} catch (error) {
state.log(
"warn",
"Failed to resolve installed apps service from Aurelia container.",
error?.stack || String(error)
)
return null
}
}
export function getStoreRef(state, container, webpackRequire) {
const storeCtor = findExportedConstructor(webpackRequire, (prototype) => {
return (
typeof prototype.dispatch === "function" &&
typeof prototype.registerAction === "function" &&
typeof prototype.unregisterAction === "function"
)
})
if (!storeCtor) {
state.log("warn", "Store constructor not found in webpack cache.")
return null
}
try {
const store = container.get(storeCtor)
const storeState =
typeof store.state?.getValue === "function"
? store.state.getValue()
: null
const installedAppsCount = isRecord(storeState?.installedApps)
? Object.keys(storeState.installedApps).length
: -1
const catalogGamesCount = isRecord(storeState?.catalog?.games)
? Object.keys(storeState.catalog.games).length
: -1
state.log(
"info",
"Store resolved via fallback.",
`ctor=${storeCtor.name || "<anon>"}, installedApps=${installedAppsCount}, catalogGames=${catalogGamesCount}`
)
return store
} catch (error) {
state.log(
"warn",
"Failed to resolve Store from container.",
error?.stack || String(error)
)
return null
}
}
function resolveOptionalService(state, container, webpackRequire, spec) {
const matchesMethods = (target) => hasAllMethods(target, spec.methods)
const ctor = findExportedConstructor(webpackRequire, matchesMethods)
if (ctor) {
return getContainerService(state, container, ctor, spec.stateKey, spec.label)
}
return findFallbackService(
state,
container,
spec.stateKey,
`${spec.label} constructor not found in webpack cache.`,
matchesMethods
)
}
function hasAllMethods(target, methods) {
if (!target) {
return false
}
for (const method of methods) {
if (typeof target[method] !== "function") {
return false
}
}
return true
}
function getTrainerLaunchRequestCtor(state, webpackRequire) {
const cache = webpackRequire?.c
if (!cache || typeof cache !== "object") {
warnMissingOptionalService(
state,
"trainerLaunchRequestCtor",
"Trainer launch request constructor cache is unavailable."
)
return null
}
for (const record of Object.values(cache)) {
const exports = record?.exports
if (!isRecord(exports)) {
continue
}
const candidate = exports[TRAINER_LAUNCH_REQUEST_EXPORT_KEY]
if (
typeof candidate === "function" &&
typeof exports.ZS === "function" &&
typeof exports.jR === "function" &&
typeof exports.UY === "function"
) {
clearMissingOptionalServiceWarning(state, "trainerLaunchRequestCtor")
return candidate
}
}
warnMissingOptionalService(
state,
"trainerLaunchRequestCtor",
"Trainer launch request constructor not found in webpack cache."
)
return null
}
function getContainerService(state, container, ctor, warningKey, label) {
try {
const service = container.get(ctor)
clearMissingOptionalServiceWarning(state, warningKey)
state.log(
"info",
`${label} resolved.`,
`ctor=${ctor.name || "<anon>"}${service?.runningTrainer ? ", running=yes" : ""}`
)
return service
} catch (error) {
state.log(
"warn",
`Failed to resolve ${label.toLowerCase()} from Aurelia container.`,
error?.stack || String(error)
)
return null
}
}
function findFallbackService(
state,
container,
warningKey,
missingMessage,
predicate
) {
const fallbackService = findInstanceInContainerGraph(container, predicate)
if (fallbackService) {
clearMissingOptionalServiceWarning(state, warningKey)
state.log("info", `${warningKey} resolved from container graph.`)
return fallbackService
}
warnMissingOptionalService(state, warningKey, missingMessage)
return null
}
function warnMissingOptionalService(state, key, message) {
if (state.missingOptionalServiceWarnings.has(key)) {
return
}
state.missingOptionalServiceWarnings.add(key)
state.log("warn", message)
}
function clearMissingOptionalServiceWarning(state, key) {
state.missingOptionalServiceWarnings.delete(key)
}