Typings rework

This commit is contained in:
amsyarasyiq 2024-03-11 15:04:11 +08:00
parent 09d3105e05
commit b9a29aaddd
28 changed files with 293 additions and 214 deletions

View file

@ -38,6 +38,7 @@
"eslint-plugin-unused-imports": "^3.1.0",
"intl-messageformat": "^10.5.11",
"moment": "2.22.2",
"type-fest": "^4.12.0",
"typescript": "^5.4.2",
"typescript-transform-paths": "^3.4.7"
},

View file

@ -1,5 +1,5 @@
\source ./dist/wrap.ts
\import ./wrap
\source ./dist/index.d.ts
\import ./index
@bunny from BunnyObject {
api {

View file

@ -1,6 +1,6 @@
{
"name": "bunny-types",
"version": "0.0.8",
"version": "0.0.9",
"description": "Types for Bunny, a mod for Discord's mobile apps",
"author": "pylixonly",
"publishConfig": {
@ -16,13 +16,14 @@
],
"license": "BSD-3-Clause",
"dependencies": {
"@react-native-clipboard/clipboard": "1.10.0",
"@types/chroma-js": "^2.4.0",
"@types/lodash": "^4.14.191",
"@types/react": "18.2.60",
"@types/react-native": "0.72.3",
"@react-native-clipboard/clipboard": "1.10.0",
"moment": "2.22.2",
"spitroast": "^1.4.2"
"spitroast": "^1.4.2",
"type-fest": "^4.12.0"
},
"devDependencies": {
"@uwu/treetype": "^1.1.0",

View file

@ -26,6 +26,9 @@ dependencies:
spitroast:
specifier: ^1.4.2
version: 1.4.2
type-fest:
specifier: ^4.12.0
version: 4.12.0
devDependencies:
'@uwu/treetype':
@ -4451,6 +4454,11 @@ packages:
engines: {node: '>=8'}
dev: false
/type-fest@4.12.0:
resolution: {integrity: sha512-5Y2/pp2wtJk8o08G0CMkuFPCO354FGwk/vbidxrdhRGZfd0tFnb4Qb8anp9XxXriwBgVPjdWbKpGl4J9lJY2jQ==}
engines: {node: '>=16'}
dev: false
/typescript@5.3.3:
resolution: {integrity: sha512-pXWcraxM0uxAS+tN0AG/BF2TyqmHO014Z070UsJ+pFvYuRSq8KH8DmWpnbXe0pEPDHXZV3FcAbJkijJ5oNEnWw==}
engines: {node: '>=14.17'}

View file

@ -12,8 +12,14 @@ const exec = util.promisify(childProcess.exec);
await fs.copyFile("package.json", "./dist/package.json");
await fs.copyFile("LICENSE", "./dist/LICENSE");
await exec("pnpm dts-bundle-generator -o dist/index.d.ts --project ../../tsconfig.json ../../src/lib/index.ts --no-check --sort --no-banner");
await exec("pnpm dts-bundle-generator -o dist/object.d.ts --project ../../tsconfig.json ../../src/lib/index.ts --no-check --sort");
await fs.writeFile("./dist/wrap.ts", "export type BunnyObject = typeof import(\".\")");
await fs.writeFile("./dist/index.d.ts", `
declare global {
var bunny: BunnyObject;
}
export type BunnyObject = typeof import("./object");
`.trim());
await exec("pnpm treetype def.tt dist/treetype.d.ts");

View file

@ -1,3 +1,8 @@
{
"include": ["dist"]
"include": [
"dist"
],
"compilerOptions": {
"stripInternal": true
}
}

8
pnpm-lock.yaml generated
View file

@ -70,6 +70,9 @@ devDependencies:
moment:
specifier: 2.22.2
version: 2.22.2
type-fest:
specifier: ^4.12.0
version: 4.12.0
typescript:
specifier: ^5.4.2
version: 5.4.2
@ -2247,6 +2250,11 @@ packages:
engines: {node: '>=10'}
dev: true
/type-fest@4.12.0:
resolution: {integrity: sha512-5Y2/pp2wtJk8o08G0CMkuFPCO354FGwk/vbidxrdhRGZfd0tFnb4Qb8anp9XxXriwBgVPjdWbKpGl4J9lJY2jQ==}
engines: {node: '>=16'}
dev: true
/typed-array-buffer@1.0.2:
resolution: {integrity: sha512-gEymJYKZtKXzzBzM4jqa9w6Q1Jjm7x2d+sh19AdsD4wqnMPDYyvwpsIc2Q/835kHuo3BEQ7CjelGhfTsoBb2MQ==}
engines: {node: '>= 0.4'}

View file

@ -7,6 +7,7 @@ import { registerSection } from "@lib/ui/settings";
import { version } from "bunny-build";
// @ts-ignore
// eslint-disable-next-line import-alias/import-alias
import _PyoncordIcon from "../../../assets/icons/pyon64.png";
export const PyoncordIcon = _PyoncordIcon as string;

View file

@ -2,7 +2,7 @@ import { Strings } from "@core/i18n";
import { PyoncordIcon } from "@core/ui/settings";
import { getAssetIDByName } from "@lib/api/assets";
import { useProxy } from "@lib/api/storage";
import { _toggleSafeMode, getDebugInfo } from "@lib/debug";
import { getDebugInfo, toggleSafeMode } from "@lib/debug";
import { settings } from "@lib/settings";
import { Stack, TableRow, TableRowGroup, TableSwitchRow, useNavigation } from "@lib/ui/components/discord/Redesign";
import { DISCORD_SERVER, GITHUB } from "@lib/utils/constants";
@ -66,7 +66,7 @@ export default function General() {
label={settings.safeMode?.enabled ? Strings.RELOAD_IN_NORMAL_MODE : Strings.RELOAD_IN_SAFE_MODE}
subLabel={settings.safeMode?.enabled ? Strings.RELOAD_IN_NORMAL_MODE_DESC : Strings.RELOAD_IN_SAFE_MODE_DESC}
icon={<TableRow.Icon source={getAssetIDByName("ic_privacy_24px")} />}
onPress={_toggleSafeMode}
onPress={toggleSafeMode}
/>
<TableSwitchRow
label={Strings.DEVELOPER_SETTINGS}

View file

@ -18,150 +18,154 @@ import * as color from "@ui/color";
import * as components from "@ui/components";
import * as toasts from "@ui/toasts";
export const createVendettaObject = (unloads: any[]): any => ({
patcher: {
before: patcher.before,
after: patcher.after,
instead: patcher.instead
},
metro: {
find: (filter: (m: any) => boolean) => metro.find(filter),
findAll: (filter: (m: any) => boolean) => metro.findAll(filter),
findByProps: (...props: any) => metro.findByProps(...props),
findByPropsAll: (...props: any) => metro.findByPropsAll(...props),
findByName: (name: string, defaultExp?: boolean | undefined) => metro.findByName(name, defaultExp),
findByNameAll: (name: string, defaultExp?: boolean | undefined) => metro.findByNameAll(name, defaultExp),
findByDisplayName: (displayName: string, defaultExp?: boolean | undefined) => metro.findByDisplayName(displayName, defaultExp),
findByDisplayNameAll: (displayName: string, defaultExp?: boolean | undefined) => metro.findByDisplayNameAll(displayName, defaultExp),
findByTypeName: (typeName: string, defaultExp?: boolean | undefined) => metro.findByTypeName(typeName, defaultExp),
findByTypeNameAll: (typeName: string, defaultExp?: boolean | undefined) => metro.findByTypeNameAll(typeName, defaultExp),
findByStoreName: (name: string) => metro.findByStoreName(name),
common: {
constants: common.constants,
channels: common.channels,
i18n: common.i18n,
url: common.url,
toasts: common.toasts,
stylesheet: {
...CompatfulRedesign,
createThemedStyleSheet
export const initVendettaObject = (): any => {
const api = window.vendetta = {
patcher: {
before: patcher.before,
after: patcher.after,
instead: patcher.instead
},
metro: {
modules: window.modules,
find: (filter: (m: any) => boolean) => metro.find(filter),
findAll: (filter: (m: any) => boolean) => metro.findAll(filter),
findByProps: (...props: any) => metro.findByProps(...props),
findByPropsAll: (...props: any) => metro.findByPropsAll(...props),
findByName: (name: string, defaultExp?: boolean | undefined) => metro.findByName(name, defaultExp),
findByNameAll: (name: string, defaultExp?: boolean | undefined) => metro.findByNameAll(name, defaultExp),
findByDisplayName: (displayName: string, defaultExp?: boolean | undefined) => metro.findByDisplayName(displayName, defaultExp),
findByDisplayNameAll: (displayName: string, defaultExp?: boolean | undefined) => metro.findByDisplayNameAll(displayName, defaultExp),
findByTypeName: (typeName: string, defaultExp?: boolean | undefined) => metro.findByTypeName(typeName, defaultExp),
findByTypeNameAll: (typeName: string, defaultExp?: boolean | undefined) => metro.findByTypeNameAll(typeName, defaultExp),
findByStoreName: (name: string) => metro.findByStoreName(name),
common: {
constants: common.constants,
channels: common.channels,
i18n: common.i18n,
url: common.url,
toasts: common.toasts,
stylesheet: {
...CompatfulRedesign,
createThemedStyleSheet
},
clipboard: common.clipboard,
assets: common.assets,
invites: common.invites,
commands: common.commands,
navigation: common.navigation,
navigationStack: common.navigationStack,
NavigationNative: common.NavigationNative,
Flux: common.Flux,
FluxDispatcher: common.FluxDispatcher,
React: common.React,
ReactNative: common.ReactNative,
moment: common.moment,
chroma: common.chroma,
lodash: common.lodash,
util: common.util
}
},
constants: {
DISCORD_SERVER: "https://discord.gg/n9QQ4XhhJP",
GITHUB: "https://github.com/vendetta-mod",
PROXY_PREFIX: "https://vd-plugins.github.io/proxy",
HTTP_REGEX: /^https?:\/\/(?:www\.)?[-a-zA-Z0-9@:%._+~#=]{1,256}\.[a-zA-Z0-9()]{1,6}\b(?:[-a-zA-Z0-9()@:%_+.~#?&/=]*)$/,
HTTP_REGEX_MULTI: /https?:\/\/(?:www\.)?[-a-zA-Z0-9@:%._+~#=]{1,256}\.[a-zA-Z0-9()]{1,6}\b(?:[-a-zA-Z0-9()@:%_+.~#?&//=]*)/g,
DISCORD_SERVER_ID: "1015931589865246730",
PLUGINS_CHANNEL_ID: "1091880384561684561",
THEMES_CHANNEL_ID: "1091880434939482202",
},
utils: {
findInReactTree: (tree: { [key: string]: any; }, filter: any) => utils.findInReactTree(tree, filter),
findInTree: (tree: any, filter: any, options: any) => utils.findInTree(tree, filter, options),
safeFetch: (input: RequestInfo | URL, options?: RequestInit | undefined, timeout?: number | undefined) => utils.safeFetch(input, options, timeout),
unfreeze: (obj: object) => ({ ...obj }),
without: (object: any, ...keys: any) => utils.without(object, ...keys)
},
debug: {
connectToDebugger: (url: string) => debug.connectToDebugger(url),
getDebugInfo: () => debug.getDebugInfo()
},
ui: {
components: {
Forms,
General: common.ReactNative,
Alert: components.discord.Alert,
Button: components.discord.Button,
HelpMessage: components.discord.HelpMessage,
SafeAreaView: components.discord.SafeAreaView,
Summary: components.Summary,
ErrorBoundary: components.ErrorBoundary,
Codeblock: components.Codeblock,
Search: components.Search
},
clipboard: common.clipboard,
assets: common.assets,
invites: common.invites,
commands: common.commands,
navigation: common.navigation,
navigationStack: common.navigationStack,
NavigationNative: common.NavigationNative,
Flux: common.Flux,
FluxDispatcher: common.FluxDispatcher,
React: common.React,
ReactNative: common.ReactNative,
moment: common.moment,
chroma: common.chroma,
lodash: common.lodash,
util: common.util
}
},
constants: {
DISCORD_SERVER: "https://discord.gg/n9QQ4XhhJP",
GITHUB: "https://github.com/vendetta-mod",
PROXY_PREFIX: "https://vd-plugins.github.io/proxy",
HTTP_REGEX: /^https?:\/\/(?:www\.)?[-a-zA-Z0-9@:%._+~#=]{1,256}\.[a-zA-Z0-9()]{1,6}\b(?:[-a-zA-Z0-9()@:%_+.~#?&/=]*)$/,
HTTP_REGEX_MULTI: /https?:\/\/(?:www\.)?[-a-zA-Z0-9@:%._+~#=]{1,256}\.[a-zA-Z0-9()]{1,6}\b(?:[-a-zA-Z0-9()@:%_+.~#?&//=]*)/g,
DISCORD_SERVER_ID: "1015931589865246730",
PLUGINS_CHANNEL_ID: "1091880384561684561",
THEMES_CHANNEL_ID: "1091880434939482202",
},
utils: {
findInReactTree: (tree: { [key: string]: any; }, filter: any) => utils.findInReactTree(tree, filter),
findInTree: (tree: any, filter: any, options: any) => utils.findInTree(tree, filter, options),
safeFetch: (input: RequestInfo | URL, options?: RequestInit | undefined, timeout?: number | undefined) => utils.safeFetch(input, options, timeout),
unfreeze: (obj: object) => ({ ...obj }),
without: (object: any, ...keys: any) => utils.without(object, ...keys)
},
debug: {
connectToDebugger: (url: string) => debug.connectToDebugger(url),
getDebugInfo: () => debug.getDebugInfo()
},
ui: {
components: {
Forms,
General: common.ReactNative,
Alert: components.discord.Alert,
Button: components.discord.Button,
HelpMessage: components.discord.HelpMessage,
SafeAreaView: components.discord.SafeAreaView,
Summary: components.Summary,
ErrorBoundary: components.ErrorBoundary,
Codeblock: components.Codeblock,
Search: components.Search
toasts: {
showToast: (content: string, asset?: number) => toasts.showToast(content, asset)
},
alerts: {
showConfirmationAlert: (options: any) => alerts.showConfirmationAlert(options),
showCustomAlert: (component: React.ComponentType<any>, props: any) => alerts.showCustomAlert(component, props),
showInputAlert: (options: any) => alerts.showInputAlert(options)
},
assets: {
all: assets.all,
find: (filter: (a: any) => void) => assets.find(filter),
getAssetByName: (name: string) => assets.getAssetByName(name),
getAssetByID: (id: number) => assets.getAssetByID(id),
getAssetIDByName: (name: string) => assets.getAssetIDByName(name)
},
semanticColors: color.semanticColors,
rawColors: color.rawColors
},
toasts: {
showToast: (content: string, asset?: number) => toasts.showToast(content, asset)
plugins: {
plugins: plugins.plugins,
fetchPlugin: (id: string) => plugins.fetchPlugin(id),
installPlugin: (id: string, enabled?: boolean | undefined) => plugins.installPlugin(id, enabled),
startPlugin: (id: string) => plugins.startPlugin(id),
stopPlugin: (id: string, disable?: boolean | undefined) => plugins.stopPlugin(id, disable),
removePlugin: (id: string) => plugins.removePlugin(id),
getSettings: (id: string) => plugins.getSettings(id)
},
alerts: {
showConfirmationAlert: (options: any) => alerts.showConfirmationAlert(options),
showCustomAlert: (component: React.ComponentType<any>, props: any) => alerts.showCustomAlert(component, props),
showInputAlert: (options: any) => alerts.showInputAlert(options)
themes: {
themes: themes.themes,
fetchTheme: (id: string, selected?: boolean) => themes.fetchTheme(id, selected),
installTheme: (id: string) => themes.installTheme(id),
selectTheme: (id: string) => themes.selectTheme(id === "default" ? null : id),
removeTheme: (id: string) => themes.removeTheme(id),
getCurrentTheme: () => themes.getThemeFromLoader(),
updateThemes: () => themes.updateThemes()
},
assets: {
all: assets.all,
find: (filter: (a: any) => void) => assets.find(filter),
getAssetByName: (name: string) => assets.getAssetByName(name),
getAssetByID: (id: number) => assets.getAssetByID(id),
getAssetIDByName: (name: string) => assets.getAssetIDByName(name)
commands: {
registerCommand: commands.registerCommand
},
semanticColors: color.semanticColors,
rawColors: color.rawColors
},
plugins: {
plugins: plugins.plugins,
fetchPlugin: (id: string) => plugins.fetchPlugin(id),
installPlugin: (id: string, enabled?: boolean | undefined) => plugins.installPlugin(id, enabled),
startPlugin: (id: string) => plugins.startPlugin(id),
stopPlugin: (id: string, disable?: boolean | undefined) => plugins.stopPlugin(id, disable),
removePlugin: (id: string) => plugins.removePlugin(id),
getSettings: (id: string) => plugins.getSettings(id)
},
themes: {
themes: themes.themes,
fetchTheme: (id: string, selected?: boolean) => themes.fetchTheme(id, selected),
installTheme: (id: string) => themes.installTheme(id),
selectTheme: (id: string) => themes.selectTheme(id === "default" ? null : id),
removeTheme: (id: string) => themes.removeTheme(id),
getCurrentTheme: () => themes._getThemeFromLoader(),
updateThemes: () => themes.updateThemes()
},
commands: {
registerCommand: commands.registerCommand
},
storage: {
createProxy: (target: any) => storage.createProxy(target),
useProxy: <T>(_storage: T) => storage.useProxy(_storage),
createStorage: (backend: any) => storage.createStorage(backend),
wrapSync: (store: any) => storage.wrapSync(store),
awaitSyncWrapper: (store: any) => storage.awaitSyncWrapper(store),
createMMKVBackend: (store: string) => storage.createMMKVBackend(store),
createFileBackend: (file: string) => storage.createFileBackend(file)
},
settings,
loader: {
identity: getVendettaLoaderIdentity() ?? void 0,
config: loaderConfig,
},
logger: {
log: (...message: any) => console.log(...message),
info: (...message: any) => console.info(...message),
warn: (...message: any) => console.warn(...message),
error: (...message: any) => console.error(...message),
time: (...message: any) => console.time(...message),
trace: (...message: any) => console.trace(...message),
verbose: (...message: any) => console.log(...message)
},
version: debug._versionHash,
unload: () => {
unloads.filter(i => typeof i === "function").forEach(p => p());
delete window.vendetta;
},
});
storage: {
createProxy: (target: any) => storage.createProxy(target),
useProxy: <T>(_storage: T) => storage.useProxy(_storage),
createStorage: (backend: any) => storage.createStorage(backend),
wrapSync: (store: any) => storage.wrapSync(store),
awaitSyncWrapper: (store: any) => storage.awaitSyncWrapper(store),
createMMKVBackend: (store: string) => storage.createMMKVBackend(store),
createFileBackend: (file: string) => storage.createFileBackend(file)
},
settings,
loader: {
identity: getVendettaLoaderIdentity() ?? void 0,
config: loaderConfig,
},
logger: {
log: (...message: any) => console.log(...message),
info: (...message: any) => console.info(...message),
warn: (...message: any) => console.warn(...message),
error: (...message: any) => console.error(...message),
time: (...message: any) => console.time(...message),
trace: (...message: any) => console.trace(...message),
verbose: (...message: any) => console.log(...message)
},
version: debug.versionHash,
unload: () => {
delete window.vendetta;
},
};
return () => api.unload();
};

2
src/global.d.ts vendored
View file

@ -2,7 +2,7 @@ import { MetroModules } from "@lib/metro/filters";
import { BunnyObject } from "@lib/windowObject";
declare global {
type React = typeof import("react");
var React: typeof import("react");
interface Window {
[key: string]: any;

View file

@ -2,48 +2,54 @@ import initFixes from "@core/fixes";
import { initFetchI18nStrings } from "@core/i18n";
import { initCorePlugins } from "@core/plugins";
import initSettings from "@core/ui/settings";
import { _patchAssets } from "@lib/api/assets";
import { _patchCommands } from "@lib/api/commands";
import { _injectFluxInterceptor } from "@lib/api/flux";
import { initVendettaObject } from "@core/vendettaObject";
import { patchAssets } from "@lib/api/assets";
import { patchCommands } from "@lib/api/commands";
import { injectFluxInterceptor } from "@lib/api/flux";
import { isThemeSupported } from "@lib/api/native/loader";
import { _patchLogHook } from "@lib/debug";
import { _initPlugins } from "@lib/managers/plugins";
import { _initThemes, _patchChatBackground } from "@lib/managers/themes";
import { _patchSettings } from "@lib/ui/settings";
import { patchLogHook } from "@lib/debug";
import { initPlugins } from "@lib/managers/plugins";
import { initThemes, patchChatBackground } from "@lib/managers/themes";
import { patchSettings } from "@lib/ui/settings";
import { logger } from "@lib/utils/logger";
import initWindowObject from "@lib/windowObject";
import initSafeMode from "@ui/safeMode";
import * as lib from "./lib";
export default async () => {
// Themes
if (isThemeSupported()) {
try {
_initThemes();
initThemes();
} catch (e) {
console.error("[Bunny] Failed to initialize themes...", e);
}
}
// Load everything in parallel
const unloads = await Promise.all([
_injectFluxInterceptor(),
_patchSettings(),
_patchLogHook(),
_patchAssets(),
_patchCommands(),
_patchChatBackground(),
await Promise.all([
injectFluxInterceptor(),
patchSettings(),
patchLogHook(),
patchAssets(),
patchCommands(),
patchChatBackground(),
initVendettaObject(),
initFetchI18nStrings(),
initSettings(),
initFixes(),
initSafeMode(),
initCorePlugins(),
]);
]).then(
// Push them all to unloader
u => u.forEach(f => f && lib.unload.push(f))
);
// Assign window object
initWindowObject(unloads);
window.bunny = lib;
// Once done, load plugins
unloads.push(await _initPlugins());
lib.unload.push(await initPlugins());
// We good :)
logger.log("Bunny is ready!");

View file

@ -8,7 +8,10 @@ export interface Asset {
id: number;
}
export function _patchAssets() {
/**
* @internal
*/
export function patchAssets() {
const unpatch = after("registerAsset", assets, (args: Asset[], id: number) => {
const asset = args[0];
all[asset.name] = { ...asset, id: id };

View file

@ -4,7 +4,10 @@ import { commands as commandsModule } from "@metro/common";
let commands: ApplicationCommand[] = [];
export function _patchCommands() {
/**
* @internal
*/
export function patchCommands() {
const unpatch = after("getBuiltInCommands", commandsModule, ([type], res: ApplicationCommand[]) => {
if (type === ApplicationCommandType.CHAT) return res.concat(commands);
});

View file

@ -9,7 +9,10 @@ export const dispatcher = FluxDispatcher;
type Intercept = (payload: Record<string, any> & { type: string; }) => any;
let intercepts: Intercept[] = [];
export function _injectFluxInterceptor() {
/**
* @internal
*/
export function injectFluxInterceptor() {
const cb = (payload: any) => {
for (const intercept of intercepts) {
const res = intercept(payload);

View file

@ -3,4 +3,6 @@ export {
before,
instead
} from "spitroast";
/** @internal */
export * as default from "spitroast";

View file

@ -2,7 +2,7 @@ import { getAssetIDByName } from "@lib/api/assets";
import { getLoaderName, isThemeSupported } from "@lib/api/native/loader";
import { BundleUpdaterManager, ClientInfoManager, DeviceManager } from "@lib/api/native/modules";
import { after } from "@lib/api/patcher";
import { _getThemeFromLoader, selectTheme } from "@lib/managers/themes";
import { getThemeFromLoader, selectTheme } from "@lib/managers/themes";
import { settings } from "@lib/settings";
import { logger } from "@lib/utils/logger";
import { showToast } from "@ui/toasts";
@ -28,10 +28,13 @@ export interface RNConstants extends PlatformConstants {
systemName: string;
}
export async function _toggleSafeMode() {
/**
* @internal
*/
export async function toggleSafeMode() {
settings.safeMode = { ...settings.safeMode, enabled: !settings.safeMode?.enabled };
if (isThemeSupported()) {
if (_getThemeFromLoader()?.id) settings.safeMode!.currentThemeId = _getThemeFromLoader()!.id;
if (getThemeFromLoader()?.id) settings.safeMode!.currentThemeId = getThemeFromLoader()!.id;
if (settings.safeMode?.enabled) {
await selectTheme("default");
} else if (settings.safeMode?.currentThemeId) {
@ -66,7 +69,10 @@ export function connectToDebugger(url: string) {
});
}
export function _patchLogHook() {
/**
* @internal
*/
export function patchLogHook() {
const unpatch = after("nativeLoggingHook", globalThis, args => {
if (socket?.readyState === WebSocket.OPEN) socket.send(JSON.stringify({ message: args[0], level: args[1] }));
logger.log(args[0]);
@ -78,7 +84,8 @@ export function _patchLogHook() {
};
}
export const _versionHash = version;
/** @internal */
export const versionHash = version;
export function getDebugInfo() {
// Hermes
@ -93,11 +100,11 @@ export function getDebugInfo() {
return {
/** @deprecated */
vendetta: {
version: _versionHash,
version: versionHash,
loader: getLoaderName(),
},
bunny: {
version: _versionHash,
version: versionHash,
loader: getLoaderName(),
},
discord: {

View file

@ -1,3 +1,4 @@
// eslint-disable-next-line import-alias/import-alias
import "../global.d.ts";
export * as api from "./api";
@ -7,3 +8,18 @@ export * as metro from "./metro";
export * as settings from "./settings";
export * as ui from "./ui";
export * as utils from "./utils";
const _disposer = new Array<() => unknown>;
export function unload() {
for (const d of _disposer) if (typeof d === "function") d();
delete window.bunny;
}
/**
* For internal use only, do not use!
* @internal
*/
unload.push = (fn: typeof _disposer[number]) => {
_disposer.push(fn);
};

View file

@ -74,7 +74,10 @@ export async function installPlugin(id: string, enabled = true) {
if (enabled) await startPlugin(id);
}
export async function _evalPlugin(plugin: BunnyPlugin) {
/**
* @internal
*/
export async function evalPlugin(plugin: BunnyPlugin) {
const vendettaForPlugins = {
...window.vendetta,
plugin: {
@ -99,7 +102,7 @@ export async function startPlugin(id: string) {
try {
if (!settings.safeMode?.enabled) {
const pluginRet: EvaledPlugin = await _evalPlugin(plugin);
const pluginRet: EvaledPlugin = await evalPlugin(plugin);
loadedPlugins[id] = pluginRet;
pluginRet.onLoad?.();
}
@ -145,7 +148,10 @@ export async function removePlugin(id: string) {
await purgeStorage(id);
}
export async function _initPlugins() {
/**
* @internal
*/
export async function initPlugins() {
await awaitSyncWrapper(settings);
await awaitSyncWrapper(plugins);
const allIds = Object.keys(plugins);

View file

@ -69,7 +69,10 @@ async function writeTheme(theme: Theme | {}) {
await createFileBackend(getThemeFilePath() || "theme.json").set(theme);
}
export function _patchChatBackground() {
/**
* @internal
*/
export function patchChatBackground() {
const MessagesWrapperConnected = findByName("MessagesWrapperConnected", false);
if (!MessagesWrapperConnected) return;
const { MessagesWrapper } = findByProps("MessagesWrapper");
@ -204,13 +207,16 @@ export async function removeTheme(id: string) {
return theme.selected;
}
export function _getThemeFromLoader(): Theme | null {
/**
* @internal
*/
export function getThemeFromLoader(): Theme | null {
return getStoredTheme();
}
export async function updateThemes() {
await awaitSyncWrapper(themes);
const currentTheme = _getThemeFromLoader();
const currentTheme = getThemeFromLoader();
await Promise.allSettled(Object.keys(themes).map(id => fetchTheme(id, currentTheme?.id === id)));
}
@ -380,8 +386,11 @@ export function applyTheme(appliedTheme: Theme | null, fallbackTheme?: string, u
}
}
export async function _initThemes() {
const currentTheme = _getThemeFromLoader();
/**
* @internal
*/
export async function initThemes() {
const currentTheme = getThemeFromLoader();
enabled = !!currentTheme;
patchColor();

View file

@ -1,8 +1,6 @@
import { find, findByProps } from "@metro/filters";
import { Dispatcher } from "@metro/types";
const colorModule = findByProps("colors", "unsafe_rawColors");
// Discord
export const constants = findByProps("Fonts", "Permissions");
export const channels = findByProps("getVoiceChannelId");

View file

@ -6,8 +6,9 @@ export type PropIntellisense<P extends string | symbol> = Record<P, any> & Recor
export type PropsFinder = <T extends string | symbol>(...props: T[]) => PropIntellisense<T>;
export type PropsFinderAll = <T extends string | symbol>(...props: T[]) => PropIntellisense<T>[];
// Metro require
declare const __r: (moduleId: string) => any;
// Metro global vars
declare var __r: (moduleId: string) => any;
declare var modules: MetroModules;
// Function to blacklist a module, preventing it from being searched again
const blacklist = (id: string) => Object.defineProperty(window.modules, id, {
@ -102,7 +103,6 @@ const filterModules = (modules: MetroModules, single = false) => (filter: (m: an
if (!single) return found;
};
export const { modules } = window;
export const find = filterModules(modules, true);
export const findAll = filterModules(modules);

View file

@ -11,6 +11,5 @@ export {
findByStoreName,
findByTypeName,
findByTypeNameAll,
modules
} from "./filters";
export * as filters from "./filters";

View file

@ -1,7 +1,7 @@
import { Strings } from "@core/i18n";
import { DeviceManager } from "@lib/api/native/modules";
import { after } from "@lib/api/patcher";
import { _toggleSafeMode } from "@lib/debug";
import { toggleSafeMode } from "@lib/debug";
import { settings } from "@lib/settings";
import { ButtonColors } from "@lib/utils/types";
import { findByName, findByProps } from "@metro/filters";
@ -78,7 +78,7 @@ export default () => after("render", ErrorBoundary.prototype, function (this: an
// This is in the patch and not outside of it so that we can use `this`, e.g. for setting state
const buttons: Button[] = [
{ text: Strings.RELOAD_DISCORD, onPress: this.handleReload },
...!settings.safeMode?.enabled ? [{ text: Strings.RELOAD_IN_SAFE_MODE, onPress: _toggleSafeMode }] : [],
...!settings.safeMode?.enabled ? [{ text: Strings.RELOAD_IN_SAFE_MODE, onPress: toggleSafeMode }] : [],
{ text: Strings.RETRY_RENDER, color: ButtonColors.RED, onPress: () => this.setState({ info: null, error: null }) },
];

View file

@ -1,4 +1,5 @@
// Good luck reading this!
import { lazy } from "react";
import type { ImageURISource } from "react-native";
import { patchPanelUI } from "./patches/panel";
@ -8,7 +9,7 @@ export interface RowConfig {
key: string;
title: () => string;
onPress?: () => any;
render?: Parameters<typeof React.lazy>[0];
render?: Parameters<typeof lazy>[0];
icon?: ImageURISource | number;
usePredicate?: () => boolean,
rawTabsConfig?: Record<string, any>;
@ -23,7 +24,10 @@ export function registerSection(section: { name: string; items: RowConfig[]; })
return () => delete registeredSections[section.name];
}
export function _patchSettings() {
/**
* @internal
*/
export function patchSettings() {
const unpatches = new Array<() => boolean>;
patchPanelUI(unpatches);

View file

@ -1,12 +1,6 @@
import { LiteralUnion } from "type-fest";
export type StringUnion<T> = T | (string & {});
export type OptionalKeys<T extends Array<string>> = StringUnion<T[number]>;
export type ExcludeInternalProperties<T> =
T extends (...args: any[]) => any
? T : {
[K in keyof T as K extends `_${string}` ? never : K]: T[K] extends object ? ExcludeInternalProperties<T[K]> : T[K];
};
export type OptionalKeys<T extends Array<string>> = LiteralUnion<T[number], string>;
export interface Author {
name: string;

View file

@ -1,6 +0,0 @@
import { createVendettaObject } from "@core/vendettaObject";
export default function initWindowObject(unloads: any[]) {
window.vendetta = createVendettaObject(unloads);
window.bunny = require(".");
}

View file

@ -12,6 +12,7 @@
"esModuleInterop": true,
"jsx": "react",
"strict": true,
"stripInternal": true,
"forceConsistentCasingInFileNames": true,
"resolveJsonModule": true,
"skipLibCheck": true,