mirror of
https://github.com/k1tbyte/Wand-Enhancer.git
synced 2026-08-29 01:08:51 +00:00
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
This commit is contained in:
@@ -0,0 +1,254 @@
|
||||
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)
|
||||
}
|
||||
Reference in New Issue
Block a user