Files
Wand-Enhancer/web-panel/bridge/scripts/default/installed-apps-sync/runtime.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

325 lines
6.7 KiB
JavaScript

export function isRecord(value) {
return typeof value === "object" && value !== null
}
export function getRequire() {
return (
globalThis.require ||
(typeof window !== "undefined" ? window.require : null)
)
}
export function safeString(...values) {
for (const value of values) {
if (typeof value === "string" && value.trim()) {
return value.trim()
}
}
return ""
}
export function getWebpackRequire() {
const chunk = globalThis.webpackChunkWeMod
if (!Array.isArray(chunk)) {
return null
}
if (typeof chunk.__wandWebpackRequire === "function") {
return chunk.__wandWebpackRequire
}
let resolvedRequire = null
chunk.push([
[`wand-enhancer-${Date.now()}`],
{},
(webpackRequire) => {
resolvedRequire = webpackRequire
},
])
if (typeof resolvedRequire === "function") {
chunk.__wandWebpackRequire = resolvedRequire
}
return resolvedRequire
}
export function getAppRoot() {
return (
document.getElementById("root") ||
document.querySelector("[aurelia-app]") ||
document.querySelector("root")
)
}
export function hasAppRoot() {
return Boolean(getAppRoot())
}
export function getAureliaContainer() {
const root = getAppRoot()
const rootContainer = getContainerFromSubtree(root)
if (rootContainer) {
return rootContainer
}
const bodyContainer = getContainerFromElement(document.body)
if (bodyContainer) {
return bodyContainer
}
if (isRecord(globalThis.aurelia) && globalThis.aurelia.container) {
return globalThis.aurelia.container
}
return null
}
export function summarizeAureliaSubtree(root) {
if (!root) {
return "root=null"
}
let elementsWithAu = 0
let controllerEntries = 0
let namedAuEntries = 0
function inspectElement(element) {
if (!isRecord(element?.au)) {
return
}
elementsWithAu += 1
if (element.au.controller) {
controllerEntries += 1
}
for (const [key, value] of Object.entries(element.au)) {
if (key !== "controller" && isRecord(value)) {
namedAuEntries += 1
}
}
}
inspectElement(root)
const walker = document.createTreeWalker(root, NodeFilter.SHOW_ELEMENT)
let element = walker.nextNode()
while (element) {
inspectElement(element)
element = walker.nextNode()
}
return `elementsWithAu=${elementsWithAu}, controllerEntries=${controllerEntries}, namedAuEntries=${namedAuEntries}`
}
export function findExportedConstructor(webpackRequire, predicate) {
const cache = webpackRequire?.c
if (!cache || typeof cache !== "object") {
return null
}
for (const record of Object.values(cache)) {
const exports = record?.exports
const candidates = []
if (typeof exports === "function") {
candidates.push(exports)
} else if (isRecord(exports)) {
if (typeof exports.default === "function") {
candidates.push(exports.default)
}
for (const value of Object.values(exports)) {
if (typeof value === "function") {
candidates.push(value)
}
}
}
for (const candidate of candidates) {
if (candidate?.prototype && predicate(candidate.prototype)) {
return candidate
}
}
}
return null
}
export function findInstanceInContainerGraph(root, predicate, maxDepth = 4) {
if (!root) {
return null
}
const seen = new Set()
const queue = [{ value: root, depth: 0 }]
while (queue.length > 0) {
const current = queue.shift()
const value = current?.value
const depth = current?.depth ?? 0
if (!value || seen.has(value)) {
continue
}
seen.add(value)
try {
if (predicate(value)) {
return value
}
} catch (error) {}
if (depth >= maxDepth) {
continue
}
enqueueNestedValues(queue, seen, value, depth + 1)
}
return null
}
export function getBasename(location) {
if (typeof location !== "string" || !location.trim()) {
return ""
}
const normalized = location.replace(/\\/g, "/").replace(/\/+$/, "")
const leaf = normalized.split("/").filter(Boolean).pop()
return leaf ? leaf.trim() : ""
}
export function toStringId(value) {
if (typeof value === "string" && value.trim()) {
return value.trim()
}
if (typeof value === "number" && Number.isFinite(value)) {
return String(value)
}
return null
}
export function normalizeStringList(value) {
if (!Array.isArray(value)) {
return []
}
return value
.filter((entry) => typeof entry === "string" && entry.trim())
.map((entry) => entry.trim())
}
export function getPreferredLocale() {
return safeString(
document.documentElement?.lang,
Array.isArray(globalThis.navigator?.languages)
? globalThis.navigator.languages.find(
(entry) => typeof entry === "string" && entry.trim()
)
: "",
globalThis.navigator?.language,
"en-US"
)
}
function getContainerFromAu(au) {
if (!isRecord(au)) {
return null
}
if (au.container) {
return au.container
}
const directControllerContainer =
au.controller?.container || au.controller?.viewModel?.container
if (directControllerContainer) {
return directControllerContainer
}
for (const value of Object.values(au)) {
if (!isRecord(value)) {
continue
}
const container =
value.container ||
value.controller?.container ||
value.viewModel?.container ||
value.controller?.viewModel?.container
if (container) {
return container
}
}
return null
}
function getContainerFromElement(element) {
if (!element) {
return null
}
if (element.__aurelia__?.container) {
return element.__aurelia__.container
}
return getContainerFromAu(element.au)
}
function getContainerFromSubtree(root) {
if (!root) {
return null
}
const rootContainer = getContainerFromElement(root)
if (rootContainer) {
return rootContainer
}
const walker = document.createTreeWalker(root, NodeFilter.SHOW_ELEMENT)
let element = walker.nextNode()
while (element) {
const container = getContainerFromElement(element)
if (container) {
return container
}
element = walker.nextNode()
}
return null
}
function enqueueNestedValues(queue, seen, value, depth) {
if (value instanceof Map) {
enqueueIterable(queue, seen, value.values(), depth)
return
}
if (value instanceof Set || Array.isArray(value)) {
enqueueIterable(queue, seen, value.values(), depth)
return
}
if (!isRecord(value) && typeof value !== "function") {
return
}
enqueueIterable(queue, seen, Object.values(value), depth)
}
function enqueueIterable(queue, seen, values, depth) {
for (const entry of values) {
if ((isRecord(entry) || typeof entry === "function") && !seen.has(entry)) {
queue.push({ value: entry, depth })
}
}
}