GoonCord/src/index.ts
kmmiio99o bd9df78849
perf: add startup timings & asset/storage optimizations
Add `src/lib/utils/timings.ts` and instrument critical inits in
`src/index.ts` Cache asset iteration (`src/lib/api/assets/index.ts`)
with short TTL + invalidation Lazy-require object-observer in
`src/lib/api/storage/index.ts` to defer startup cost
2025-12-22 16:32:23 +01:00

130 lines
5.1 KiB
TypeScript

import patchErrorBoundary from "@core/debug/patches/patchErrorBoundary";
import initFixes from "@core/fixes";
import { initFetchI18nStrings } from "@core/i18n";
import initSettings from "@core/ui/settings";
import { initVendettaObject } from "@core/vendetta/api";
import { updateFonts } from "@lib/addons/fonts";
import { initThemes } from "@lib/addons/themes";
import { patchCommands } from "@lib/api/commands";
import { patchLogHook } from "@lib/api/debug";
import { injectFluxInterceptor } from "@lib/api/flux";
import { patchJsx } from "@lib/api/react/jsx";
import { logger } from "@lib/utils/logger";
import { patchSettings } from "@ui/settings";
import { updaterSettings } from "@lib/api/settings";
import { InteractionManager } from "react-native";
import { getDebugInfo, initDebugger } from "@lib/api/debug";
// Debug toggle helper (temporary runtime fallback). The helper is dynamically
// imported when needed (to avoid bundling it permanently) and removed after use.
import * as lib from "./lib";
import { timings } from "@lib/utils/timings";
/**
* Start sequence split into critical (UI) and deferred (network/plugin) work.
* The goal is to get the UI ready quickly and run heavy tasks after interactions.
*/
export default async () => {
// Wrap critical initializers as named functions so we can instrument each.
const criticalInitFns: Array<[string, () => Promise<any>]> = [
["initThemes", () => initThemes()],
["injectFluxInterceptor", () => injectFluxInterceptor()],
["patchSettings", () => patchSettings()],
["patchLogHook", () => patchLogHook()],
["patchCommands", () => patchCommands()],
["patchJsx", () => patchJsx()],
["patchErrorBoundary", () => patchErrorBoundary()],
["initVendettaObject", () => initVendettaObject()],
["initFetchI18nStrings", () => initFetchI18nStrings()],
["initSettings", () => initSettings()],
["initFixes", () => initFixes()],
["initDebugger", () => initDebugger()],
];
// Run critical inits with timing instrumentation and collect unpatchers/cleanup handlers.
await Promise.all(
criticalInitFns.map(([name, fn]) =>
timings.measureAsync(`critical:${name}`, async () => fn()),
),
)
.then((u) => u.forEach((f) => f && lib.unload.push(f)))
.catch((e) => {
// Log but don't abort — critical inits failing should be visible in logs.
console.warn("Critical initialization error:", e);
});
// Expose the library object early so UI and other code can access window.bunny.
window.bunny = lib;
logger.log(
"ShiggyCord: UI-critical initialization complete — deferring plugin & network work",
);
// Deferred work: run after interactions to avoid blocking initial paint and navigation.
const runDeferred = async () => {
const { VdPluginManager } = await import("@core/vendetta/plugins");
const { initPlugins, updatePlugins } = await import("@lib/addons/plugins");
// Initialize Vendetta plugins (may start many plugins) — do not block UI.
VdPluginManager.initPlugins()
.then((u) => lib.unload.push(u))
.catch((e) => logger.log("Vendetta init failed:", e));
// Start ShiggyCord (Bunny) plugins now without forcing repository updates.
// Plugin repository fetching is deferred so the app can finish launching first.
// Stagger plugin startup to reduce CPU/memory spikes: use smaller batches and a small interval.
// This keeps the UI responsive while plugins initialize in the background.
initPlugins({ staggerInterval: 500, batchSize: 2 });
// Attempt a lightweight recovery toggle if some core plugins failed to start.
try {
// Dynamically import the helper (if present) but suppress verbose errors.
const mod = await import("@core/debug/toggleCorePlugins").catch(
() => null,
);
if (mod?.default) {
// Run helper with minimal noise; ignore failures.
mod.default({ offDuration: 1500 }).catch(() => {});
}
// Try to remove the helper source file (best-effort, ignore failures).
await import("@lib/api/native/fs")
.then((fs) => fs.removeFile("src/core/debug/toggleCorePlugins.ts"))
.catch(() => {});
} catch {
// suppressed
}
// Update fonts in background
updateFonts().catch((e) => logger.log("updateFonts failed:", e));
// Schedule plugin repository update after a delay (5 minutes) so update work
// does not impact initial launch performance.
setTimeout(
() => {
updatePlugins().catch((e) =>
logger.log("updatePlugins failed (deferred 5min):", e),
);
},
5 * 60 * 1000,
);
// Note: we intentionally moved the call to `updatePlugins()` above so core
// plugins are registered prior to calling `initPlugins()`.
};
// Preferred: wait until interactions finish (animations / navigation).
try {
InteractionManager.runAfterInteractions(() => {
// small delay to ensure native lifecycle settled
setTimeout(runDeferred, 50);
});
} catch (e) {
// Fallback if InteractionManager isn't available for any reason.
setTimeout(runDeferred, 200);
}
// Final ready log for basic UI availability.
logger.log("ShiggyCord is ready.");
};