diff --git a/shims/depsModule.ts b/shims/depsModule.ts index f6268b1..8df1d12 100644 --- a/shims/depsModule.ts +++ b/shims/depsModule.ts @@ -1,4 +1,4 @@ -import { findByPropsLazy } from "@metro"; +import { findByPropsLazy } from "@metro/wrappers"; module.exports = { "react": findByPropsLazy("createElement"), diff --git a/shims/jsxRuntime.ts b/shims/jsxRuntime.ts index b5c6355..7352a67 100644 --- a/shims/jsxRuntime.ts +++ b/shims/jsxRuntime.ts @@ -1,5 +1,5 @@ import { getProxyFactory } from "@lib/utils/lazy"; -import { findByPropsLazy } from "@metro/utils"; +import { findByPropsLazy } from "@metro/wrappers"; const jsxRuntime = findByPropsLazy("jsx", "jsxs", "Fragment"); diff --git a/src/core/commands/debug.ts b/src/core/commands/debug.ts index 9bc2ed1..fcbd166 100644 --- a/src/core/commands/debug.ts +++ b/src/core/commands/debug.ts @@ -1,6 +1,6 @@ import { Strings } from "@core/i18n"; import { ApplicationCommand, ApplicationCommandOptionType } from "@lib/api/commands/types"; -import { getDebugInfo } from "@lib/debug"; +import { getDebugInfo } from "@lib/api/debug"; import { messageUtil } from "@metro/common"; export default () => { diff --git a/src/core/commands/eval.ts b/src/core/commands/eval.ts index 8d9d0dd..0d17c85 100644 --- a/src/core/commands/eval.ts +++ b/src/core/commands/eval.ts @@ -1,8 +1,8 @@ import { Strings } from "@core/i18n"; import { ApplicationCommand, ApplicationCommandOptionType } from "@lib/api/commands/types"; -import { settings } from "@lib/settings"; +import { settings } from "@lib/api/settings"; import { messageUtil } from "@metro/common"; -import { findByPropsLazy } from "@metro/utils"; +import { findByPropsLazy } from "@metro/wrappers"; const util = findByPropsLazy("inspect"); const AsyncFunction = (async () => void 0).constructor; diff --git a/src/core/i18n/index.ts b/src/core/i18n/index.ts index a7df857..17962a2 100644 --- a/src/core/i18n/index.ts +++ b/src/core/i18n/index.ts @@ -1,5 +1,5 @@ import { FluxDispatcher } from "@metro/common"; -import { findByNameLazy } from "@metro/utils"; +import { findByNameLazy } from "@metro/wrappers"; import { PrimitiveType } from "intl-messageformat"; import langDefault from "./default.json"; diff --git a/src/core/plugins/index.ts b/src/core/plugins/index.ts index 6b24449..6b2e1ea 100644 --- a/src/core/plugins/index.ts +++ b/src/core/plugins/index.ts @@ -1,10 +1,20 @@ +import { PluginInstanceInternal } from "@lib/plugins/types"; -export function initCorePlugins() { - const unloads = [ - require("./quickInstall") - ].map(p => { - return p.default(); - }); +interface CorePlugin { + default: PluginInstanceInternal; + preenabled: boolean; +} + +// Called from @lib/plugins +export const getCorePlugins = (): Record => ({ + "vd-quick-install": require("./vd-quick-install") +}); - return () => unloads.forEach(m => typeof m === "function" && m()); +/** + * @internal + */ +export function defineCorePlugin(instance: PluginInstanceInternal): PluginInstanceInternal { + // @ts-expect-error + instance[Symbol.for("bunny.core.plugin")] = true; + return instance; } diff --git a/src/core/plugins/quickInstall/index.ts b/src/core/plugins/quickInstall/index.ts deleted file mode 100644 index a687a05..0000000 --- a/src/core/plugins/quickInstall/index.ts +++ /dev/null @@ -1,11 +0,0 @@ -import patchForumPost from "./forumPost"; -import patchUrl from "./url"; - -export default function onLoad() { - const patches = new Array; - - patches.push(patchForumPost()); - patches.push(patchUrl()); - - return () => patches.forEach(p => p()); -} diff --git a/src/core/plugins/quickInstall/forumPost.tsx b/src/core/plugins/vd-quick-install/forumPost.tsx similarity index 97% rename from src/core/plugins/quickInstall/forumPost.tsx rename to src/core/plugins/vd-quick-install/forumPost.tsx index a40f603..2698dbd 100644 --- a/src/core/plugins/quickInstall/forumPost.tsx +++ b/src/core/plugins/vd-quick-install/forumPost.tsx @@ -4,11 +4,11 @@ import { findAssetId } from "@lib/api/assets"; import { isThemeSupported } from "@lib/api/native/loader"; import { after } from "@lib/api/patcher"; import { useProxy } from "@lib/api/storage"; -import { installTheme, removeTheme, themes } from "@lib/managers/themes"; +import { installTheme, removeTheme, themes } from "@lib/themes"; import { DISCORD_SERVER_ID, HTTP_REGEX_MULTI, PLUGINS_CHANNEL_ID, THEMES_CHANNEL_ID, VD_PROXY_PREFIX } from "@lib/utils/constants"; import { lazyDestructure } from "@lib/utils/lazy"; import { Button } from "@metro/common/components"; -import { findByProps, findByPropsLazy } from "@metro/utils"; +import { findByProps, findByPropsLazy } from "@metro/wrappers"; import { ErrorBoundary } from "@ui/components"; import { showToast } from "@ui/toasts"; diff --git a/src/core/plugins/vd-quick-install/index.ts b/src/core/plugins/vd-quick-install/index.ts new file mode 100644 index 0000000..931c228 --- /dev/null +++ b/src/core/plugins/vd-quick-install/index.ts @@ -0,0 +1,21 @@ +import { defineCorePlugin } from ".."; +import patchForumPost from "./forumPost"; +import patchUrl from "./url"; + +let patches = [] as (() => unknown)[]; + +export default defineCorePlugin({ + manifest: { + id: "bunny.quick.install", + name: "QuickInstall", + version: "1.0.0", + description: "Quickly install Vendetta plugins and themes", + authors: [{ name: "pylixonly" }] + }, + start() { + patches = [patchForumPost(), patchUrl()]; + }, + stop() { + patches.forEach(p => p()); + } +}); diff --git a/src/core/plugins/quickInstall/url.tsx b/src/core/plugins/vd-quick-install/url.tsx similarity index 96% rename from src/core/plugins/quickInstall/url.tsx rename to src/core/plugins/vd-quick-install/url.tsx index d7a073a..25899fe 100644 --- a/src/core/plugins/quickInstall/url.tsx +++ b/src/core/plugins/vd-quick-install/url.tsx @@ -3,13 +3,13 @@ import { VdPluginManager } from "@core/vendetta/plugins"; import { findAssetId } from "@lib/api/assets"; import { isThemeSupported } from "@lib/api/native/loader"; import { after, instead } from "@lib/api/patcher"; -import { installTheme } from "@lib/managers/themes"; +import { installTheme } from "@lib/themes"; import { THEMES_CHANNEL_ID, VD_PROXY_PREFIX } from "@lib/utils/constants"; import { lazyDestructure } from "@lib/utils/lazy"; import { channels, url } from "@metro/common"; import { byMutableProp } from "@metro/filters"; import { findExports } from "@metro/finders"; -import { findByProps, findByPropsLazy } from "@metro/utils"; +import { findByProps, findByPropsLazy } from "@metro/wrappers"; import { showConfirmationAlert } from "@ui/alerts"; import { showToast } from "@ui/toasts"; diff --git a/src/core/polyfills/allSettled.ts b/src/core/polyfills/allSettled.ts deleted file mode 100644 index 82f1f7c..0000000 --- a/src/core/polyfills/allSettled.ts +++ /dev/null @@ -1,8 +0,0 @@ -//! Around 202.4, Promise.allSettled was undefined due to conflicting then/promise versions(?), so we use our own. -const allSettledFulfill = (value: T) => ({ status: "fulfilled", value }); -const allSettledReject = (reason: T) => ({ status: "rejected", reason }); -const mapAllSettled = (item: T) => Promise.resolve(item).then(allSettledFulfill, allSettledReject); -export const allSettled = (iterator: T) => Promise.all(Array.from(iterator).map(mapAllSettled)); - -// @ts-ignore -Promise.allSettled ??= allSettled; diff --git a/src/core/polyfills/promise-all-settled.ts b/src/core/polyfills/promise-all-settled.ts new file mode 100644 index 0000000..3124777 --- /dev/null +++ b/src/core/polyfills/promise-all-settled.ts @@ -0,0 +1,7 @@ +const allSettledFulfill = (value: T) => ({ status: "fulfilled", value } as const); +const allSettledReject = (reason: T) => ({ status: "rejected", reason } as const); +const mapAllSettled = (item: T) => Promise.resolve(item).then(allSettledFulfill, allSettledReject); + +Promise.allSettled ??= (iterator: T) => { + return Promise.all(Array.from(iterator).map(mapAllSettled)); +}; diff --git a/src/core/ui/components/AddonCard.tsx b/src/core/ui/components/AddonCard.tsx index 927334a..c6a4da6 100644 --- a/src/core/ui/components/AddonCard.tsx +++ b/src/core/ui/components/AddonCard.tsx @@ -1,7 +1,7 @@ import { findAssetId } from "@lib/api/assets"; import { lazyDestructure } from "@lib/utils/lazy"; import { Card, FormRadio, FormSwitch, IconButton, LegacyFormRow, Stack, Text } from "@metro/common/components"; -import { findByProps } from "@metro/utils"; +import { findByProps } from "@metro/wrappers"; import { semanticColors } from "@ui/color"; import { createStyles, TextStyleSheet } from "@ui/styles"; import { TouchableOpacity, View } from "react-native"; diff --git a/src/core/ui/components/AddonPage.tsx b/src/core/ui/components/AddonPage.tsx index d57fa3e..6f09fe5 100644 --- a/src/core/ui/components/AddonPage.tsx +++ b/src/core/ui/components/AddonPage.tsx @@ -2,7 +2,7 @@ import { Strings } from "@core/i18n"; import { CardWrapper } from "@core/ui/components/AddonCard"; import { findAssetId } from "@lib/api/assets"; import { useProxy } from "@lib/api/storage"; -import { settings } from "@lib/settings"; +import { settings } from "@lib/api/settings"; import { HTTP_REGEX_MULTI } from "@lib/utils/constants"; import { findByProps } from "@metro"; import { clipboard } from "@metro/common"; diff --git a/src/core/ui/settings/index.ts b/src/core/ui/settings/index.ts index 7433740..55c5642 100644 --- a/src/core/ui/settings/index.ts +++ b/src/core/ui/settings/index.ts @@ -3,7 +3,7 @@ import { Strings } from "@core/i18n"; import { findAssetId } from "@lib/api/assets"; import { isFontSupported, isThemeSupported } from "@lib/api/native/loader"; import { useProxy } from "@lib/api/storage"; -import { settings } from "@lib/settings"; +import { settings } from "@lib/api/settings"; import { registerSection } from "@ui/settings"; import { version } from "bunny-build-info"; diff --git a/src/core/ui/settings/pages/Developer/index.tsx b/src/core/ui/settings/pages/Developer/index.tsx index c340bf2..7681b2e 100644 --- a/src/core/ui/settings/pages/Developer/index.tsx +++ b/src/core/ui/settings/pages/Developer/index.tsx @@ -4,12 +4,12 @@ import AssetBrowser from "@core/ui/settings/pages/Developer/AssetBrowser"; import { findAssetId } from "@lib/api/assets"; import { getReactDevToolsProp, getReactDevToolsVersion, isLoaderConfigSupported, isReactDevToolsPreloaded, isVendettaLoader } from "@lib/api/native/loader"; import { useProxy } from "@lib/api/storage"; -import { connectToDebugger } from "@lib/debug"; -import { loaderConfig, settings } from "@lib/settings"; +import { connectToDebugger } from "@lib/api/debug"; +import { loaderConfig, settings } from "@lib/api/settings"; import { lazyDestructure } from "@lib/utils/lazy"; import { NavigationNative } from "@metro/common"; import { Button, LegacyFormText, Stack, TableRow, TableRowGroup, TableSwitchRow, TextInput } from "@metro/common/components"; -import { findByProps } from "@metro/utils"; +import { findByProps } from "@metro/wrappers"; import { semanticColors } from "@ui/color"; import { ErrorBoundary } from "@ui/components"; import { createStyles, TextStyleSheet } from "@ui/styles"; diff --git a/src/core/ui/settings/pages/Fonts/FontCard.tsx b/src/core/ui/settings/pages/Fonts/FontCard.tsx index 0a39d38..5b9d8bb 100644 --- a/src/core/ui/settings/pages/Fonts/FontCard.tsx +++ b/src/core/ui/settings/pages/Fonts/FontCard.tsx @@ -3,9 +3,9 @@ import { CardWrapper } from "@core/ui/components/AddonCard"; import { findAssetId } from "@lib/api/assets"; import { BundleUpdaterManager } from "@lib/api/native/modules"; import { useProxy } from "@lib/api/storage"; -import { FontDefinition, fonts, selectFont } from "@lib/managers/fonts"; -import { showConfirmationAlert } from "@lib/ui/alerts"; -import { TextStyleSheet } from "@lib/ui/styles"; +import { FontDefinition, fonts, selectFont } from "@lib/fonts"; +import { showConfirmationAlert } from "@ui/alerts"; +import { TextStyleSheet } from "@ui/styles"; import { lazyDestructure } from "@lib/utils/lazy"; import { ButtonColors } from "@lib/utils/types"; import { findByProps } from "@metro"; diff --git a/src/core/ui/settings/pages/Fonts/FontEditor.tsx b/src/core/ui/settings/pages/Fonts/FontEditor.tsx index b75c51a..1df14a7 100644 --- a/src/core/ui/settings/pages/Fonts/FontEditor.tsx +++ b/src/core/ui/settings/pages/Fonts/FontEditor.tsx @@ -1,12 +1,12 @@ import { formatString, Strings } from "@core/i18n"; import { findAssetId } from "@lib/api/assets"; import { createProxy, useProxy } from "@lib/api/storage"; -import { FontDefinition, fonts, removeFont, saveFont, validateFont } from "@lib/managers/fonts"; -import { getCurrentTheme } from "@lib/managers/themes"; +import { FontDefinition, fonts, removeFont, saveFont, validateFont } from "@lib/fonts"; +import { getCurrentTheme } from "@lib/themes"; import { safeFetch } from "@lib/utils"; import { NavigationNative } from "@metro/common"; import { ActionSheet, BottomSheetTitleHeader, Button, IconButton, Stack, TableRow, TableRowGroup, Text, TextInput } from "@metro/common/components"; -import { findByPropsLazy } from "@metro/utils"; +import { findByPropsLazy } from "@metro/wrappers"; import { ErrorBoundary } from "@ui/components"; import { useMemo, useRef, useState } from "react"; import { ScrollView, View } from "react-native"; diff --git a/src/core/ui/settings/pages/Fonts/index.tsx b/src/core/ui/settings/pages/Fonts/index.tsx index ffa9633..a1eb018 100644 --- a/src/core/ui/settings/pages/Fonts/index.tsx +++ b/src/core/ui/settings/pages/Fonts/index.tsx @@ -2,8 +2,8 @@ import { Strings } from "@core/i18n"; import AddonPage from "@core/ui/components/AddonPage"; import FontEditor from "@core/ui/settings/pages/Fonts/FontEditor"; import { useProxy } from "@lib/api/storage"; -import { FontDefinition, fonts, installFont } from "@lib/managers/fonts"; -import { settings } from "@lib/settings"; +import { FontDefinition, fonts, installFont } from "@lib/fonts"; +import { settings } from "@lib/api/settings"; import { NavigationNative } from "@metro/common"; import FontCard from "./FontCard"; diff --git a/src/core/ui/settings/pages/General/About.tsx b/src/core/ui/settings/pages/General/About.tsx index f067be2..6848290 100644 --- a/src/core/ui/settings/pages/General/About.tsx +++ b/src/core/ui/settings/pages/General/About.tsx @@ -1,8 +1,8 @@ import { Strings } from "@core/i18n"; import Version from "@core/ui/settings/pages/General/Version"; import { useProxy } from "@lib/api/storage"; -import { getDebugInfo } from "@lib/debug"; -import { settings } from "@lib/settings"; +import { getDebugInfo } from "@lib/api/debug"; +import { settings } from "@lib/api/settings"; import { Stack, TableRowGroup } from "@metro/common/components"; import { Platform, ScrollView } from "react-native"; diff --git a/src/core/ui/settings/pages/General/index.tsx b/src/core/ui/settings/pages/General/index.tsx index ef80c84..7485bb4 100644 --- a/src/core/ui/settings/pages/General/index.tsx +++ b/src/core/ui/settings/pages/General/index.tsx @@ -3,8 +3,8 @@ import { PyoncordIcon } from "@core/ui/settings"; import About from "@core/ui/settings/pages/General/About"; import { findAssetId } from "@lib/api/assets"; import { useProxy } from "@lib/api/storage"; -import { getDebugInfo, toggleSafeMode } from "@lib/debug"; -import { settings } from "@lib/settings"; +import { getDebugInfo, toggleSafeMode } from "@lib/api/debug"; +import { settings } from "@lib/api/settings"; import { DISCORD_SERVER, GITHUB } from "@lib/utils/constants"; import { NavigationNative, url } from "@metro/common"; import { Stack, TableRow, TableRowGroup, TableSwitchRow } from "@metro/common/components"; diff --git a/src/core/ui/settings/pages/Plugins/PluginCard.tsx b/src/core/ui/settings/pages/Plugins/PluginCard.tsx index 7d1c44d..2062a68 100644 --- a/src/core/ui/settings/pages/Plugins/PluginCard.tsx +++ b/src/core/ui/settings/pages/Plugins/PluginCard.tsx @@ -2,7 +2,7 @@ import { CardWrapper } from "@core/ui/components/AddonCard"; import { VdPluginManager, VendettaPlugin } from "@core/vendetta/plugins"; import { findAssetId } from "@lib/api/assets"; import { useProxy } from "@lib/api/storage"; -import { showSheet } from "@lib/ui/sheets"; +import { showSheet } from "@ui/sheets"; import { lazyDestructure } from "@lib/utils/lazy"; import { findByProps } from "@metro"; import { NavigationNative } from "@metro/common"; diff --git a/src/core/ui/settings/pages/Plugins/index.tsx b/src/core/ui/settings/pages/Plugins/index.tsx index 0143fc6..c4afdfb 100644 --- a/src/core/ui/settings/pages/Plugins/index.tsx +++ b/src/core/ui/settings/pages/Plugins/index.tsx @@ -3,7 +3,7 @@ import AddonPage from "@core/ui/components/AddonPage"; import PluginCard from "@core/ui/settings/pages/Plugins/PluginCard"; import { VdPluginManager, VendettaPlugin } from "@core/vendetta/plugins"; import { useProxy } from "@lib/api/storage"; -import { settings } from "@lib/settings"; +import { settings } from "@lib/api/settings"; import { Author } from "@lib/utils/types"; export default function Plugins() { diff --git a/src/core/ui/settings/pages/Plugins/sheets/PluginInfoActionSheet.tsx b/src/core/ui/settings/pages/Plugins/sheets/PluginInfoActionSheet.tsx index a28c20e..e742408 100644 --- a/src/core/ui/settings/pages/Plugins/sheets/PluginInfoActionSheet.tsx +++ b/src/core/ui/settings/pages/Plugins/sheets/PluginInfoActionSheet.tsx @@ -2,9 +2,9 @@ import { formatString, Strings } from "@core/i18n"; import { VdPluginManager, VendettaPlugin } from "@core/vendetta/plugins"; import { findAssetId } from "@lib/api/assets"; import { purgeStorage, useProxy } from "@lib/api/storage"; -import { showConfirmationAlert } from "@lib/ui/alerts"; -import { hideSheet } from "@lib/ui/sheets"; -import { showToast } from "@lib/ui/toasts"; +import { showConfirmationAlert } from "@ui/alerts"; +import { hideSheet } from "@ui/sheets"; +import { showToast } from "@ui/toasts"; import { ButtonColors } from "@lib/utils/types"; import { clipboard } from "@metro/common"; import { ActionSheet, ActionSheetRow, Button, TableRow, Text } from "@metro/common/components"; diff --git a/src/core/ui/settings/pages/Plugins/usePluginCardStyles.ts b/src/core/ui/settings/pages/Plugins/usePluginCardStyles.ts index c575fee..90982f4 100644 --- a/src/core/ui/settings/pages/Plugins/usePluginCardStyles.ts +++ b/src/core/ui/settings/pages/Plugins/usePluginCardStyles.ts @@ -1,4 +1,4 @@ -import { createStyles } from "@lib/ui/styles"; +import { createStyles } from "@ui/styles"; import { tokens } from "@metro/common"; export const usePluginCardStyles = createStyles({ diff --git a/src/core/ui/settings/pages/Themes/ThemeCard.tsx b/src/core/ui/settings/pages/Themes/ThemeCard.tsx index c60243a..42fef99 100644 --- a/src/core/ui/settings/pages/Themes/ThemeCard.tsx +++ b/src/core/ui/settings/pages/Themes/ThemeCard.tsx @@ -2,8 +2,8 @@ import { formatString, Strings } from "@core/i18n"; import AddonCard, { CardWrapper } from "@core/ui/components/AddonCard"; import { findAssetId } from "@lib/api/assets"; import { useProxy } from "@lib/api/storage"; -import { applyTheme, fetchTheme, removeTheme, selectTheme, Theme, themes } from "@lib/managers/themes"; -import { settings } from "@lib/settings"; +import { settings } from "@lib/api/settings"; +import { applyTheme, fetchTheme, removeTheme, selectTheme, Theme, themes } from "@lib/themes"; import { ButtonColors } from "@lib/utils/types"; import { clipboard } from "@metro/common"; import { showConfirmationAlert } from "@ui/alerts"; @@ -18,7 +18,7 @@ function selectAndApply(value: boolean, theme: Theme) { } } -export default function ThemeCard({ item: theme, index }: CardWrapper) { +export default function ThemeCard({ item: theme }: CardWrapper) { useProxy(theme); const [removed, setRemoved] = React.useState(false); @@ -30,7 +30,6 @@ export default function ThemeCard({ item: theme, index }: CardWrapper) { return ( i.name).join(", ")}` : ""} descriptionLabel={theme.data.description ?? "No description."} diff --git a/src/core/ui/settings/pages/Themes/index.tsx b/src/core/ui/settings/pages/Themes/index.tsx index 98fcf6e..5b90edf 100644 --- a/src/core/ui/settings/pages/Themes/index.tsx +++ b/src/core/ui/settings/pages/Themes/index.tsx @@ -2,8 +2,8 @@ import { formatString, Strings } from "@core/i18n"; import AddonPage from "@core/ui/components/AddonPage"; import ThemeCard from "@core/ui/settings/pages/Themes/ThemeCard"; import { useProxy } from "@lib/api/storage"; -import { installTheme, Theme, themes } from "@lib/managers/themes"; -import { settings } from "@lib/settings"; +import { settings } from "@lib/api/settings"; +import { installTheme, Theme, themes } from "@lib/themes"; import { Author } from "@lib/utils/types"; import { Button } from "@metro/common/components"; diff --git a/src/core/vendetta/api.tsx b/src/core/vendetta/api.tsx index a13a3d5..d23c77b 100644 --- a/src/core/vendetta/api.tsx +++ b/src/core/vendetta/api.tsx @@ -4,9 +4,9 @@ import { getVendettaLoaderIdentity, isPyonLoader } from "@lib/api/native/loader" import patcher from "@lib/api/patcher"; import * as storage from "@lib/api/storage"; import { createStorage } from "@lib/api/storage"; -import * as debug from "@lib/debug"; -import * as themes from "@lib/managers/themes"; -import { loaderConfig, settings } from "@lib/settings"; +import * as debug from "@lib/api/debug"; +import { loaderConfig, settings } from "@lib/api/settings"; +import * as themes from "@lib/themes"; import * as utils from "@lib/utils"; import { cyrb64Hash } from "@lib/utils/cyrb64"; import { DiscordLogger } from "@lib/utils/logger"; @@ -39,6 +39,15 @@ export async function createVdPluginObject(plugin: VendettaPlugin) { } export const initVendettaObject = (): any => { + // pitfall: this assumes the returning module(s) are the same within the same location + // find(m => m.render?.name === "ActionSheet") - would work fine + // ["trackThis", "trackThat"].forEach(p => find(m => m[p])) - would not + const createStackBasedFilter = (fn: any) => { + return (filter: (m: any) => boolean) => { + return fn(metro.factories.createSimpleFilter(filter, cyrb64Hash(new Error().stack!))); + }; + }; + const api = window.vendetta = { patcher: { before: patcher.before, @@ -47,12 +56,8 @@ export const initVendettaObject = (): any => { }, metro: { modules: window.modules, - find: (filter: (m: any) => boolean) => { - return metro.findExports(metro.createSimpleFilter(filter, cyrb64Hash(new Error().stack!))); - }, - findAll: (filter: (m: any) => boolean) => { - return metro.findAllExports(metro.createSimpleFilter(filter, cyrb64Hash(new Error().stack!))); - }, + find: createStackBasedFilter(metro.findExports), + findAll: createStackBasedFilter(metro.findAllExports), findByProps: (...props: any[]) => { // TODO: remove this hack to fix Decor if (props.length === 1 && props[0] === "KeyboardAwareScrollView") { diff --git a/src/core/vendetta/plugins.ts b/src/core/vendetta/plugins.ts index 2559677..4b48736 100644 --- a/src/core/vendetta/plugins.ts +++ b/src/core/vendetta/plugins.ts @@ -1,6 +1,5 @@ -import { allSettled } from "@core/polyfills/allSettled"; import { awaitStorage, createMMKVBackend, createStorage, purgeStorage, wrapSync } from "@lib/api/storage"; -import { settings } from "@lib/settings"; +import { settings } from "@lib/api/settings"; import { safeFetch } from "@lib/utils"; import { BUNNY_PROXY_PREFIX, VD_PROXY_PREFIX } from "@lib/utils/constants"; import { DiscordLogger, logger } from "@lib/utils/logger"; @@ -168,7 +167,7 @@ export const VdPluginManager = { if (!settings.safeMode?.enabled) { // Loop over any plugin that is enabled, update it if allowed, then start it. - await allSettled(allIds.filter(pl => plugins[pl].enabled).map(async pl => (plugins[pl].update && await this.fetchPlugin(pl).catch((e: Error) => logger.error(e.message)), await this.startPlugin(pl)))); + await Promise.allSettled(allIds.filter(pl => plugins[pl].enabled).map(async pl => (plugins[pl].update && await this.fetchPlugin(pl).catch((e: Error) => logger.error(e.message)), await this.startPlugin(pl)))); // Wait for the above to finish, then update all disabled plugins that are allowed to. allIds.filter(pl => !plugins[pl].enabled && plugins[pl].update).forEach(pl => this.fetchPlugin(pl)); } diff --git a/src/entry.ts b/src/entry.ts index 3231677..56e8381 100644 --- a/src/entry.ts +++ b/src/entry.ts @@ -9,7 +9,10 @@ async function initializeBunny() { // Make 'freeze' and 'seal' do nothing Object.freeze = Object.seal = Object; - await require("@metro/caches").initMetroCache(); + // Polyfill Promise.allSettled + require("@core/polyfills/promise-all-settled"); + + await require("@metro/internals/caches").initMetroCache(); await require(".").default(); } catch (e) { const { ClientInfoManager } = require("@lib/api/native/modules"); diff --git a/src/index.ts b/src/index.ts index fe18c15..ca44b85 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,6 +1,5 @@ import initFixes from "@core/fixes"; import { initFetchI18nStrings } from "@core/i18n"; -import { initCorePlugins } from "@core/plugins"; import initSettings from "@core/ui/settings"; import { initVendettaObject } from "@core/vendetta/api"; import { VdPluginManager } from "@core/vendetta/plugins"; @@ -9,10 +8,10 @@ import { injectFluxInterceptor } from "@lib/api/flux"; import { removeFile, writeFile } from "@lib/api/native/fs"; import { isPyonLoader, isThemeSupported } from "@lib/api/native/loader"; import { FileManager } from "@lib/api/native/modules"; -import { patchLogHook } from "@lib/debug"; -import { updateFonts } from "@lib/managers/fonts"; -import { initThemes, patchChatBackground } from "@lib/managers/themes"; +import { patchLogHook } from "@lib/api/debug"; +import { updateFonts } from "@lib/fonts"; import { checkAndRegisterUpdates, initPlugins } from "@lib/plugins"; +import { initThemes, patchChatBackground } from "@lib/themes"; import { logger } from "@lib/utils/logger"; import initSafeMode from "@ui/safeMode"; import { patchSettings } from "@ui/settings"; @@ -48,7 +47,6 @@ export default async () => { initSettings(), initFixes(), initSafeMode(), - initCorePlugins(), checkAndRegisterUpdates() ]).then( // Push them all to unloader diff --git a/src/lib/api/assets.ts b/src/lib/api/assets.ts index 91831ca..2abdb1e 100644 --- a/src/lib/api/assets.ts +++ b/src/lib/api/assets.ts @@ -1,6 +1,6 @@ import { after } from "@lib/api/patcher"; -import { getMetroCache, indexAssetName } from "@metro/caches"; -import { getImportingModuleId, requireModule } from "@metro/modules"; +import { getMetroCache, indexAssetName } from "@metro/internals/caches"; +import { getImportingModuleId, requireModule } from "@metro/internals/modules"; // TODO: Deprecate this map, make another that maps to an array of assets (Asset[]) instead /** @@ -78,7 +78,7 @@ export function findAsset(id: number): Asset | undefined; export function findAsset(name: string): Asset | undefined; export function findAsset(filter: (a: Asset) => boolean): Asset | undefined; -export function findAsset(param: number | string | ((a: any) => void)) { +export function findAsset(param: number | string | ((a: Asset) => boolean)) { if (typeof param === "number") return assetsModule.getAssetByID(param); if (typeof param === "string") return assetsMap[param]; return Object.values(assetsMap).find(param); diff --git a/src/lib/debug.ts b/src/lib/api/debug.ts similarity index 95% rename from src/lib/debug.ts rename to src/lib/api/debug.ts index 37606de..1e02e35 100644 --- a/src/lib/debug.ts +++ b/src/lib/api/debug.ts @@ -2,8 +2,8 @@ import { findAssetId } from "@lib/api/assets"; import { getLoaderName, getLoaderVersion, isThemeSupported } from "@lib/api/native/loader"; import { BundleUpdaterManager, ClientInfoManager, DeviceManager } from "@lib/api/native/modules"; import { after } from "@lib/api/patcher"; -import { getThemeFromLoader, selectTheme, themes } from "@lib/managers/themes"; -import { settings } from "@lib/settings"; +import { settings } from "@lib/api/settings"; +import { getThemeFromLoader, selectTheme, themes } from "@lib/themes"; import { logger } from "@lib/utils/logger"; import { showToast } from "@ui/toasts"; import { version } from "bunny-build-info"; @@ -98,9 +98,11 @@ export function getDebugInfo() { const rnVer = PlatformConstants.reactNativeVersion; return { - /** @deprecated */ + /** + * @deprecated use `bunny` field + * */ vendetta: { - version: versionHash, + version: versionHash.split("-")[0], loader: getLoaderName(), }, bunny: { diff --git a/src/lib/api/native/loader.ts b/src/lib/api/native/loader.ts index 4bbc7e8..b8be1d0 100644 --- a/src/lib/api/native/loader.ts +++ b/src/lib/api/native/loader.ts @@ -1,4 +1,4 @@ -import { Theme } from "@lib/managers/themes"; +import { Theme } from "@lib/themes"; // @ts-ignore const pyonLoaderIdentity = globalThis.__PYON_LOADER__; @@ -58,7 +58,7 @@ function polyfillVendettaLoaderIdentity() { const id = getStoredTheme()?.id; if (!id) return null; - const { themes } = require("@lib/managers/themes"); + const { themes } = require("@lib/themes"); return themes[id] ?? getStoredTheme() ?? null; }, configurable: true diff --git a/src/lib/settings.ts b/src/lib/api/settings.ts similarity index 100% rename from src/lib/settings.ts rename to src/lib/api/settings.ts diff --git a/src/lib/api/storage/index.ts b/src/lib/api/storage/index.ts index 62cea96..207441e 100644 --- a/src/lib/api/storage/index.ts +++ b/src/lib/api/storage/index.ts @@ -1,5 +1,5 @@ import { StorageBackend } from "@lib/api/storage/backends"; -import { Emitter } from "@lib/utils/emitter"; +import { Emitter } from "@lib/utils/Emitter"; const emitterSymbol = Symbol.for("vendetta.storage.emitter"); const syncAwaitSymbol = Symbol.for("vendetta.storage.accessor"); diff --git a/src/lib/api/storage/new.ts b/src/lib/api/storage/new.ts index c704339..0061037 100644 --- a/src/lib/api/storage/new.ts +++ b/src/lib/api/storage/new.ts @@ -1,6 +1,6 @@ // New iteration of storage API, mostly yoinked from unreleased pyoncord (and sunrise?) import { fileExists, readFile, writeFile } from "@lib/api/native/fs"; -import { Emitter } from "@lib/utils/emitter"; +import { Emitter } from "@lib/utils/Emitter"; import invariant from "@lib/utils/invariant"; interface StorageBackend { diff --git a/src/lib/managers/fonts.ts b/src/lib/fonts/index.ts similarity index 100% rename from src/lib/managers/fonts.ts rename to src/lib/fonts/index.ts diff --git a/src/lib/index.ts b/src/lib/index.ts index e4fd499..a882707 100644 --- a/src/lib/index.ts +++ b/src/lib/index.ts @@ -2,15 +2,16 @@ import "../global.d.ts"; // eslint-disable-line import-alias/import-alias import "../modules.d.ts"; // eslint-disable-line import-alias/import-alias export * as api from "./api"; -export * as debug from "./debug"; -export * as managers from "./managers"; +export * as debug from "./api/debug.js"; +export * as settings from "./api/settings.js"; +export * as fonts from "./fonts"; export * as plugins from "./plugins"; -export * as settings from "./settings"; +export * as themes from "./themes"; export * as ui from "./ui"; export * as utils from "./utils"; export * as metro from "@metro"; -const _disposer = new Array<() => unknown>; +const _disposer = [] as Array<() => unknown>; export function unload() { for (const d of _disposer) if (typeof d === "function") d(); diff --git a/src/lib/managers/index.ts b/src/lib/managers/index.ts deleted file mode 100644 index fd140e1..0000000 --- a/src/lib/managers/index.ts +++ /dev/null @@ -1,3 +0,0 @@ -export * as fonts from "./fonts"; -export * as plugins from "./plugins"; -export * as themes from "./themes"; diff --git a/src/lib/managers/plugins.ts b/src/lib/managers/plugins.ts deleted file mode 100644 index f40387b..0000000 --- a/src/lib/managers/plugins.ts +++ /dev/null @@ -1,228 +0,0 @@ -import { allSettled } from "@core/polyfills/allSettled"; -import { createVdPluginObject } from "@core/vendetta/api"; -import { awaitStorage, createMMKVBackend, createStorage, purgeStorage, wrapSync } from "@lib/api/storage"; -import { settings } from "@lib/settings"; -import { safeFetch } from "@lib/utils"; -import { BUNNY_PROXY_PREFIX, OLD_BUNNY_PROXY_PREFIX, VD_PROXY_PREFIX } from "@lib/utils/constants"; -import invariant from "@lib/utils/invariant"; -import { logger } from "@lib/utils/logger"; -import { Author } from "@lib/utils/types"; -import { isNotNil, uniqWith } from "es-toolkit"; - -type EvaledPlugin = { - onLoad?(): void; - onUnload(): void; - settings: () => JSX.Element; -}; - -interface PluginManifest { - id: string; - name: string; - description: string; - authors: Author[]; - main: string; - hash: string; - // Vendor-specific field, contains our own data - vendetta?: { - icon?: string; - }; - bunny?: {}; -} - -export interface BunnyPlugin { - id: string; - source: string; - manifest: PluginManifest; - enabled: boolean; - update: boolean; - /** - * Message of plugin startup error. Gone after plugin boots successfully - * */ - error?: string; - - // TODO: Use fs to avoid unnecessary memory usage - js: string; -} - -const arePluginsEnabled = () => !settings.safeMode?.enabled; - -const _pluginInstances: Record = {}; - -export const sourceStore = wrapSync(createStorage<{ [id in string]?: BunnyPlugin }>(createMMKVBackend("PLUGIN_SOURCES_STORE"))); -export const preferredSourceStore = wrapSync(createStorage<{ [id in string]?: string }>(createMMKVBackend("PREFERRED_PLUGIN_SOURCE"))); - -export function getPluginById(id: string) { - if (!id) return undefined; - - if (!preferredSourceStore[id]) { - for (const plugin of Object.values(sourceStore)) { - if (plugin?.id === id) { - return plugin; - } - } - } - - return sourceStore[preferredSourceStore[id]!]; -} - -export async function fetchAndStorePlugin(source: string) { - if (!source.endsWith("/")) source += "/"; - const existingPlugin = sourceStore[source]; - - const fetch = (url: string) => safeFetch( - url - .replace(VD_PROXY_PREFIX, BUNNY_PROXY_PREFIX) - .replace(OLD_BUNNY_PROXY_PREFIX, BUNNY_PROXY_PREFIX), - { cache: "no-store" } - ); - - let pluginManifest: PluginManifest; - - try { - pluginManifest = await (await fetch(source + "manifest.json")).json(); - } catch { - throw new Error(`Failed to fetch manifest for ${source}`); - } - - for (const f of ["id", "main", "hash", "bunny"] as const) - invariant(pluginManifest[f], `Plugin manifest does not contain mandatory field: '${f}'`); - - let pluginJs: string | undefined; - - if (existingPlugin?.manifest.hash !== pluginManifest.hash) { - try { - pluginJs = await (await fetch(source + pluginManifest.main)).text(); - } catch { } // Empty catch, checked below - } - - invariant(pluginJs || existingPlugin, `Failed to fetch JS from ${source}`); - - return sourceStore[source] = { - id: pluginManifest.id, - source: source, - manifest: pluginManifest, - enabled: existingPlugin?.enabled ?? false, - update: existingPlugin?.update ?? true, - js: pluginJs ?? existingPlugin!.js, - error: existingPlugin?.error - }; -} - -export async function installPlugin(source: string, enabled = true) { - if (!source.endsWith("/")) source += "/"; - invariant(!(source in sourceStore), "Source was already installed"); - - const plugin = await fetchAndStorePlugin(source); - if (enabled) await startPlugin(plugin.id); -} - -/** - * @internal - */ -async function evalPlugin(plugin: BunnyPlugin) { - const vdObject = await createVdPluginObject(plugin); - const pluginString = `vendetta=>{return ${plugin.js}}\n//# sourceURL=${plugin.source}?hash=${plugin.manifest.hash}`; - - const raw = (0, eval)(pluginString)(vdObject); - const ret = typeof raw === "function" ? raw() : raw; - return ret?.default ?? ret ?? {}; -} - -export async function startPlugin(id: string) { - const plugin = getPluginById(id); - invariant(plugin, "Attempted to start non-existent plugin"); - - try { - if (arePluginsEnabled()) { - const pluginRet: EvaledPlugin = await evalPlugin(plugin); - _pluginInstances[id] = pluginRet; - pluginRet.onLoad?.(); - } - - delete plugin.error; - plugin.enabled = true; - } catch (e) { - logger.error(`Plugin ${plugin.source} errored whilst loading, and will be unloaded`, e); - plugin.error = e instanceof Error ? e.stack : String(e); - - try { - _pluginInstances[id]?.onUnload?.(); - } catch (e2) { - logger.error(`Plugin ${plugin.source} errored whilst unloading`, e2); - } - - delete _pluginInstances[id]; - plugin.enabled = false; - } -} - -export function stopPlugin(id: string, disable = true) { - const plugin = getPluginById(id); - const pluginInstance = _pluginInstances[id]; - invariant(plugin, "Attempted to stop non-existent plugin"); - - if (arePluginsEnabled()) { - try { - pluginInstance?.onUnload?.(); - } catch (e) { - logger.error(`Plugin ${plugin.source} errored whilst unloading`, e); - } - - delete _pluginInstances[id]; - } - - if (disable) plugin.enabled = false; -} - -export async function removePlugin(id: string) { - const plugin = getPluginById(id); - invariant(plugin, "Removing non-existent plugin"); - if (plugin.enabled) stopPlugin(id); - delete sourceStore[plugin.source]; - await purgeStorage(plugin.source); -} - -/** - * @internal - */ -export async function initPlugins() { - await awaitStorage(sourceStore, preferredSourceStore, settings); - - if (arePluginsEnabled()) { - const plugins = uniqWith( - Object.values(sourceStore) - .map(s => getPluginById(s!.id)) - .filter(isNotNil), - (a, b) => a?.id === b?.id - ); - - const updatePromise: Promise[] = []; - const updateAndStart = async (plugin: BunnyPlugin) => { - if (plugin.update) { - try { - await fetchAndStorePlugin(plugin.id); - } catch (e) { - logger.error(e); - } - } - - await startPlugin(plugin.id); - }; - - for (const plugin of plugins) { - if (plugin.enabled) { - updatePromise.push(updateAndStart(plugin)); - } else if (plugin.update) { - fetchAndStorePlugin(plugin.id); - } - } - - await allSettled(updatePromise); - } - - return () => Object.keys(_pluginInstances).forEach(p => stopPlugin(p, false)); -} - -export function getSettingsComponent(id: string) { - return _pluginInstances[id]?.settings; -} diff --git a/src/lib/plugins/index.ts b/src/lib/plugins/index.ts index 3bcc33d..ee60f6a 100644 --- a/src/lib/plugins/index.ts +++ b/src/lib/plugins/index.ts @@ -1,4 +1,4 @@ -import { allSettled } from "@core/polyfills/allSettled"; +import { getCorePlugins } from "@core/plugins"; import { readFile, removeFile, writeFile } from "@lib/api/native/fs"; import { awaitStorage, createStorage, preloadStorageIfExists, updateStorageAsync } from "@lib/api/storage/new"; import { safeFetch } from "@lib/utils"; @@ -344,7 +344,7 @@ export function stopPlugin(id: string) { export async function checkAndRegisterUpdates() { await awaitStorage(pluginRepositories, pluginSettings); - const corePlugins = {} as Record; // TODO: getCorePlugins(); + const corePlugins = getCorePlugins(); for (const id in corePlugins) { const { default: instance, @@ -361,7 +361,7 @@ export async function checkAndRegisterUpdates() { corePluginInstances.set(id, instance); } - await allSettled(Object.keys(pluginRepositories).map(async repo => { + await Promise.allSettled(Object.keys(pluginRepositories).map(async repo => { await updateRepository(repo); })); } @@ -373,7 +373,7 @@ export async function initPlugins() { await awaitStorage(pluginRepositories, pluginSettings); // Now, start all enabled plugins... - await allSettled([...registeredPlugins.keys()].map(async id => { + await Promise.allSettled([...registeredPlugins.keys()].map(async id => { if (isPluginEnabled(id)) { await startPlugin(id); } diff --git a/src/lib/plugins/types.ts b/src/lib/plugins/types.ts index 60e8392..75a3b4c 100644 --- a/src/lib/plugins/types.ts +++ b/src/lib/plugins/types.ts @@ -22,6 +22,7 @@ export interface PluginSettingsStorage { } export interface BunnyPluginManifest { + readonly id: string; readonly name: string; readonly description: string; readonly version: string; diff --git a/src/lib/managers/themes.ts b/src/lib/themes/index.ts similarity index 98% rename from src/lib/managers/themes.ts rename to src/lib/themes/index.ts index 1589c94..122b494 100644 --- a/src/lib/managers/themes.ts +++ b/src/lib/themes/index.ts @@ -2,7 +2,6 @@ * Theming system in Bunny is currently a prototype, expect an unreadable theme implementation below */ -import { allSettled } from "@core/polyfills/allSettled"; import { getStoredTheme, getThemeFilePath } from "@lib/api/native/loader"; import { ThemeManager } from "@lib/api/native/modules"; import { after, before, instead } from "@lib/api/patcher"; @@ -12,7 +11,7 @@ import { lazyDestructure, proxyLazy } from "@lib/utils/lazy"; import { Author } from "@lib/utils/types"; import { byMutableProp } from "@metro/filters"; import { createLazyModule } from "@metro/lazy"; -import { findByNameLazy, findByProps, findByPropsLazy, findByStoreNameLazy } from "@metro/utils"; +import { findByNameLazy, findByProps, findByPropsLazy, findByStoreNameLazy } from "@metro/wrappers"; import chroma from "chroma-js"; import { ImageBackground, Platform, processColor } from "react-native"; @@ -232,7 +231,7 @@ export function getThemeFromLoader(): Theme | null { export async function updateThemes() { await awaitStorage(themes); const currentTheme = getThemeFromLoader(); - await allSettled(Object.keys(themes).map(id => fetchTheme(id, currentTheme?.id === id))); + await Promise.allSettled(Object.keys(themes).map(id => fetchTheme(id, currentTheme?.id === id))); } export function getCurrentTheme() { diff --git a/src/lib/ui/alerts.ts b/src/lib/ui/alerts.ts index 892182f..e6f8c32 100644 --- a/src/lib/ui/alerts.ts +++ b/src/lib/ui/alerts.ts @@ -1,5 +1,5 @@ import { ButtonColors } from "@lib/utils/types"; -import { findByPropsLazy } from "@metro/utils"; +import { findByPropsLazy } from "@metro/wrappers"; import InputAlert, { InputAlertProps } from "@ui/components/InputAlert"; const Alerts = findByPropsLazy("openLazy", "close"); diff --git a/src/lib/ui/color.ts b/src/lib/ui/color.ts index b683386..65a3e49 100644 --- a/src/lib/ui/color.ts +++ b/src/lib/ui/color.ts @@ -1,6 +1,6 @@ -import { color } from "@lib/managers/themes"; +import { color } from "@lib/themes"; import { constants } from "@metro/common"; -import { findByStoreNameLazy } from "@metro/utils"; +import { findByStoreNameLazy } from "@metro/wrappers"; //! This module is only found on 165.0+, under the assumption that iOS 165.0 is the same as Android 165.0. //* In 167.1, most if not all traces of the old color modules were removed. diff --git a/src/lib/ui/components/ContextMenu.tsx b/src/lib/ui/components/ContextMenu.tsx index 09c98a0..7f0486f 100644 --- a/src/lib/ui/components/ContextMenu.tsx +++ b/src/lib/ui/components/ContextMenu.tsx @@ -1,7 +1,7 @@ import { instead } from "@lib/api/patcher"; import { lazyDestructure } from "@lib/utils/lazy"; import { OptionalKeys } from "@lib/utils/types"; -import { findByProps } from "@metro/utils"; +import { findByProps } from "@metro/wrappers"; import { AccessibilityInfo, Platform, View } from "react-native"; const { ContextMenu: _ContextMenu } = lazyDestructure(() => findByProps("ContextMenu")); diff --git a/src/lib/ui/components/InputAlert.tsx b/src/lib/ui/components/InputAlert.tsx index d607f06..53136c9 100644 --- a/src/lib/ui/components/InputAlert.tsx +++ b/src/lib/ui/components/InputAlert.tsx @@ -1,6 +1,6 @@ import { ButtonColors } from "@lib/utils/types"; import { LegacyAlert, LegacyFormInput } from "@metro/common/components"; -import { findByPropsLazy } from "@metro/utils"; +import { findByPropsLazy } from "@metro/wrappers"; const Alerts = findByPropsLazy("openLazy", "close"); diff --git a/src/lib/ui/safeMode.tsx b/src/lib/ui/safeMode.tsx index af707a8..25f4f88 100644 --- a/src/lib/ui/safeMode.tsx +++ b/src/lib/ui/safeMode.tsx @@ -1,14 +1,14 @@ import { Strings } from "@core/i18n"; import { DeviceManager } from "@lib/api/native/modules"; import { after } from "@lib/api/patcher"; -import { toggleSafeMode } from "@lib/debug"; -import { settings } from "@lib/settings"; +import { toggleSafeMode } from "@lib/api/debug"; +import { settings } from "@lib/api/settings"; import { lazyDestructure } from "@lib/utils/lazy"; import { ButtonColors } from "@lib/utils/types"; import { Button, CompatButton, SafeAreaView } from "@metro/common/components"; import { _lazyContextSymbol } from "@metro/lazy"; import { LazyModuleContext } from "@metro/types"; -import { findByNameLazy, findByProps } from "@metro/utils"; +import { findByNameLazy, findByProps } from "@metro/wrappers"; import { semanticColors } from "@ui/color"; import { Codeblock, ErrorBoundary as _ErrorBoundary } from "@ui/components"; import { createThemedStyleSheet, TextStyleSheet } from "@ui/styles"; diff --git a/src/lib/ui/settings/patches/panel.tsx b/src/lib/ui/settings/patches/panel.tsx index 88316f2..a10380e 100644 --- a/src/lib/ui/settings/patches/panel.tsx +++ b/src/lib/ui/settings/patches/panel.tsx @@ -2,7 +2,7 @@ import { after } from "@lib/api/patcher"; import { findInReactTree } from "@lib/utils"; import { i18n, NavigationNative } from "@metro/common"; import { LegacyFormIcon, LegacyFormRow, LegacyFormSection } from "@metro/common/components"; -import { findByNameLazy } from "@metro/utils"; +import { findByNameLazy } from "@metro/wrappers"; import { registeredSections } from "@ui/settings"; import { CustomPageRenderer, wrapOnPress } from "./shared"; diff --git a/src/lib/ui/settings/patches/shared.tsx b/src/lib/ui/settings/patches/shared.tsx index 9335190..ad564da 100644 --- a/src/lib/ui/settings/patches/shared.tsx +++ b/src/lib/ui/settings/patches/shared.tsx @@ -1,5 +1,5 @@ import { NavigationNative } from "@metro/common"; -import { findByPropsLazy } from "@metro/utils"; +import { findByPropsLazy } from "@metro/wrappers"; import { ErrorBoundary } from "@ui/components"; import { RowConfig } from "@ui/settings"; diff --git a/src/lib/ui/settings/patches/tabs.tsx b/src/lib/ui/settings/patches/tabs.tsx index 6f428ac..e7f6100 100644 --- a/src/lib/ui/settings/patches/tabs.tsx +++ b/src/lib/ui/settings/patches/tabs.tsx @@ -1,7 +1,7 @@ import { after } from "@lib/api/patcher"; import { findInReactTree } from "@lib/utils"; import { i18n } from "@metro/common"; -import { findByNameLazy, findByPropsLazy } from "@metro/utils"; +import { findByNameLazy, findByPropsLazy } from "@metro/wrappers"; import { registeredSections } from "@ui/settings"; import { CustomPageRenderer, wrapOnPress } from "./shared"; diff --git a/src/lib/ui/sheets.ts b/src/lib/ui/sheets.ts index bbe0a0e..08d7872 100644 --- a/src/lib/ui/sheets.ts +++ b/src/lib/ui/sheets.ts @@ -1,4 +1,4 @@ -import { findByPropsLazy } from "@metro/utils"; +import { findByPropsLazy } from "@metro/wrappers"; const actionSheet = findByPropsLazy("openLazy", "hideActionSheet"); diff --git a/src/lib/ui/styles.ts b/src/lib/ui/styles.ts index 546c84c..c3bb9e5 100644 --- a/src/lib/ui/styles.ts +++ b/src/lib/ui/styles.ts @@ -1,5 +1,5 @@ import { lazyDestructure, proxyLazy } from "@lib/utils/lazy"; -import { findByProps, findByPropsLazy } from "@metro/utils"; +import { findByProps, findByPropsLazy } from "@metro/wrappers"; import { isSemanticColor, resolveSemanticColor } from "@ui/color"; import { DiscordTextStyles } from "@ui/types"; import { ImageStyle, StyleSheet, TextStyle, ViewStyle } from "react-native"; diff --git a/src/lib/ui/toasts.ts b/src/lib/ui/toasts.ts index d02c395..e0c008d 100644 --- a/src/lib/ui/toasts.ts +++ b/src/lib/ui/toasts.ts @@ -2,7 +2,7 @@ import { Strings } from "@core/i18n"; import { findAssetId } from "@lib/api/assets"; import { lazyDestructure } from "@lib/utils/lazy"; import { toasts } from "@metro/common"; -import { findByProps } from "@metro/utils"; +import { findByProps } from "@metro/wrappers"; const { uuid4 } = lazyDestructure(() => findByProps("uuid4")); diff --git a/src/lib/utils/emitter.ts b/src/lib/utils/Emitter.ts similarity index 100% rename from src/lib/utils/emitter.ts rename to src/lib/utils/Emitter.ts diff --git a/src/lib/utils/index.ts b/src/lib/utils/index.ts index a457fea..0f86895 100644 --- a/src/lib/utils/index.ts +++ b/src/lib/utils/index.ts @@ -1,7 +1,7 @@ // Makes mass-importing utils cleaner, chosen over moving utils to one file export * as constants from "@lib/utils/constants"; -export { Emitter } from "@lib/utils/emitter"; +export { Emitter } from "@lib/utils/Emitter"; export { findInReactTree } from "@lib/utils/findInReactTree"; export { findInTree } from "@lib/utils/findInTree"; export * as logger from "@lib/utils/logger"; diff --git a/src/lib/utils/logger.ts b/src/lib/utils/logger.ts index 7522db3..44b519c 100644 --- a/src/lib/utils/logger.ts +++ b/src/lib/utils/logger.ts @@ -1,4 +1,4 @@ -import { findByNameLazy } from "@metro/utils"; +import { findByNameLazy } from "@metro/wrappers"; export type LoggerFunction = (...messages: any[]) => void; export interface Logger { diff --git a/src/metro/common/components.ts b/src/metro/common/components.ts index 213a345..f0e8e2a 100644 --- a/src/metro/common/components.ts +++ b/src/metro/common/components.ts @@ -1,7 +1,8 @@ /* eslint-disable prefer-destructuring */ import { lazyDestructure, proxyLazy } from "@lib/utils/lazy"; +import { createFilterDefinition } from "@metro/factories"; import { findExports } from "@metro/finders"; -import { createFilterDefinition, findByDisplayNameLazy, findByNameLazy, findByProps, findByPropsLazy } from "@metro/utils"; +import { findByDisplayNameLazy, findByNameLazy, findByProps, findByPropsLazy } from "@metro/wrappers"; import * as t from "./types/components"; diff --git a/src/metro/common/index.ts b/src/metro/common/index.ts index fce9804..0f6f27a 100644 --- a/src/metro/common/index.ts +++ b/src/metro/common/index.ts @@ -1,7 +1,6 @@ -import { proxyLazy } from "@lib/utils/lazy"; -import { findByFilePath, findByProps, findByPropsLazy } from "@metro/utils"; +import { findByFilePathLazy, findByProps, findByPropsLazy } from "@metro/wrappers"; -import { Dispatcher } from "./types"; +import type { Dispatcher } from "./types/flux"; // Discord export const constants = findByPropsLazy("Fonts", "Permissions"); @@ -13,7 +12,7 @@ export const assets = findByPropsLazy("registerAsset"); export const invites = findByPropsLazy("acceptInviteAndTransitionToInviteChannel"); export const commands = findByPropsLazy("getBuiltInCommands"); export const navigation = findByPropsLazy("pushLazy"); -export const toasts = proxyLazy(() => findByFilePath("modules/toast/native/ToastActionCreators.tsx").default); +export const toasts = findByFilePathLazy("modules/toast/native/ToastActionCreators.tsx", true); export const messageUtil = findByPropsLazy("sendBotMessage"); export const navigationStack = findByPropsLazy("createStackNavigator"); export const NavigationNative = findByPropsLazy("NavigationContainer"); diff --git a/src/metro/common/types/index.ts b/src/metro/common/types/flux.ts similarity index 100% rename from src/metro/common/types/index.ts rename to src/metro/common/types/flux.ts diff --git a/src/metro/factories.ts b/src/metro/factories.ts new file mode 100644 index 0000000..3807dcd --- /dev/null +++ b/src/metro/factories.ts @@ -0,0 +1,41 @@ + +import { FilterCheckDef, FilterDefinition, ModuleExports } from "./types"; + +export function createFilterDefinition( + fn: FilterCheckDef, + uniqMaker: (args: A) => string +): FilterDefinition { + function createHolder(func: T, args: A, raw: boolean) { + return Object.assign(func, { + filter: fn, + raw, + uniq: [ + raw && "raw::", + uniqMaker(args) + ].filter(Boolean).join("") + }); + } + + const curry = (raw: boolean) => (...args: A) => { + return createHolder((m: ModuleExports, id: number, defaultCheck: boolean) => { + return fn(args, m, id, defaultCheck); + }, args, raw); + }; + + return Object.assign(curry(false), { + byRaw: curry(true), + uniqMaker + }); +} + +export function createSimpleFilter( + filter: (m: ModuleExports) => boolean, + uniq: string +) { + return createFilterDefinition( + (_, m) => filter(m), + () => `dynamic::${uniq}` + )(); +} + + diff --git a/src/metro/filters.ts b/src/metro/filters.ts index 0a450f2..84cc88f 100644 --- a/src/metro/filters.ts +++ b/src/metro/filters.ts @@ -1,5 +1,5 @@ -import { metroModules } from "./modules"; -import { createFilterDefinition } from "./utils"; +import { createFilterDefinition } from "./factories"; +import { metroModules } from "./internals/modules"; export const byProps = createFilterDefinition( (props, m) => props.length === 0 ? m[props[0]] : props.every(p => m[p]), @@ -26,9 +26,12 @@ export const byStoreName = createFilterDefinition<[string]>( name => `bunny.metro.byStoreName(${name})` ); -export const byFilePath = createFilterDefinition<[string]>( - ([path], _, id, defaultCheck) => !defaultCheck && metroModules[id]?.__filePath === path, - path => `bunny.metro.byFilePath(${path})` +export const byFilePath = createFilterDefinition<[string, boolean]>( + // module return depends on defaultCheck. if true, it'll return module.default, otherwise the whole module + // unlike filters like byName, defaultCheck doesn't affect the return since we don't rely on exports, but only its ID + // one could say that this is technically a hack, since defaultCheck is meant for filtering exports + ([path, exportDefault], _, id, defaultCheck) => (exportDefault === defaultCheck) && metroModules[id]?.__filePath === path, + ([path, exportDefault]) => `bunny.metro.byFilePath(${path},${exportDefault})` ); export const byMutableProp = createFilterDefinition<[string]>( diff --git a/src/metro/finders.ts b/src/metro/finders.ts index ffb91dc..b803efd 100644 --- a/src/metro/finders.ts +++ b/src/metro/finders.ts @@ -1,25 +1,28 @@ -import { getCacherForUniq } from "./caches"; -import { getModules, requireModule } from "./modules"; +import { getCacherForUniq } from "./internals/caches"; +import { getModules, requireModule } from "./internals/modules"; import { FilterFn } from "./types"; function filterExports( moduleExports: any, moduleId: number, filter: FilterFn, -): [any, boolean] | [void, false] { +) { if ( moduleExports.default && moduleExports.__esModule && filter(moduleExports.default, moduleId, true) ) { - return [filter.raw ? moduleExports : moduleExports.default, !filter.raw]; + return { + exports: filter.raw ? moduleExports : moduleExports.default, + defaultExport: !filter.raw + }; } if (!filter.raw && filter(moduleExports, moduleId, false)) { - return [moduleExports, false]; + return { exports: moduleExports, defaultExport: false }; } - return [undefined, false]; + return {}; } /** @@ -30,19 +33,19 @@ export function findModule(filter: FilterFn) { const { cacheId, finish } = getCacherForUniq(filter.uniq, false); for (const [id, moduleExports] of getModules(filter.uniq, false)) { - const [testedExports, defaultExp] = filterExports( + const { exports: testedExports, defaultExport } = filterExports( moduleExports, id, filter, ); if (testedExports !== undefined) { cacheId(id, testedExports); - return [id, defaultExp]; + return { id, defaultExport }; } } finish(true); - return []; + return {}; } /** @@ -50,7 +53,7 @@ export function findModule(filter: FilterFn) { * @param filter find calls filter once for each enumerable module's exports until it finds one where filter returns a thruthy value. */ export function findModuleId(filter: FilterFn) { - return findModule(filter)?.[0]; + return findModule(filter)?.id; } /** @@ -58,9 +61,9 @@ export function findModuleId(filter: FilterFn) { * @param filter find calls filter once for each enumerable module's exports until it finds one where filter returns a thruthy value. */ export function findExports(filter: FilterFn) { - const [id, defaultExp] = findModule(filter); + const { id, defaultExport } = findModule(filter); if (id == null) return; - return defaultExp ? requireModule(id).default : requireModule(id); + return defaultExport ? requireModule(id).default : requireModule(id); } /** @@ -69,16 +72,16 @@ export function findExports(filter: FilterFn) { */ export function findAllModule(filter: FilterFn) { const { cacheId, finish } = getCacherForUniq(filter.uniq, true); - const foundExports: [id: number, defaultExp: boolean][] = []; + const foundExports: {id: number, defaultExport: boolean}[] = []; for (const [id, moduleExports] of getModules(filter.uniq, true)) { - const [testedExports, defaultExp] = filterExports( + const { exports: testedExports, defaultExport } = filterExports( moduleExports, id, filter, ); - if (testedExports !== undefined) { - foundExports.push([id, defaultExp]); + if (testedExports !== undefined && typeof defaultExport === "boolean") { + foundExports.push({ id, defaultExport }); cacheId(id, testedExports); } } @@ -92,7 +95,7 @@ export function findAllModule(filter: FilterFn) { * @param filter findAll calls filter once for each enumerable module's exports, adding the exports to the returned array when filter returns a thruthy value. */ export function findAllModuleId(filter: FilterFn) { - return findAllModule(filter).map(e => e[0]); + return findAllModule(filter).map(e => e.id); } /** @@ -101,8 +104,9 @@ export function findAllModuleId(filter: FilterFn) { */ export function findAllExports(filter: FilterFn) { return findAllModule(filter).map(ret => { - if (!ret.length) return; - const [id, defaultExp] = ret; - return defaultExp ? requireModule(id).default : requireModule(id); + if (!ret.id) return; + + const { id, defaultExport } = ret; + return defaultExport ? requireModule(id).default : requireModule(id); }); } diff --git a/src/metro/index.ts b/src/metro/index.ts index 9d01ba2..4517f53 100644 --- a/src/metro/index.ts +++ b/src/metro/index.ts @@ -1,4 +1,6 @@ export * as common from "./common"; -export * from "./filters"; +export * as factories from "./factories"; +export * as filters from "./filters"; export * from "./finders"; -export * from "./utils"; +export * as lazy from "./lazy"; +export * from "./wrappers"; diff --git a/src/metro/caches.ts b/src/metro/internals/caches.ts similarity index 92% rename from src/metro/caches.ts rename to src/metro/internals/caches.ts index d70c112..66f5df8 100644 --- a/src/metro/caches.ts +++ b/src/metro/internals/caches.ts @@ -29,7 +29,7 @@ function buildInitCache() { // because force loading it all will results in an unexpected crash. setTimeout(() => { for (const id in window.modules) { - require("@metro/modules").requireModule(id); + require("./modules").requireModule(id); } }, 100); @@ -37,7 +37,8 @@ function buildInitCache() { return cache; } -// Store in file system +// TODO: Store in file system... is a better idea? +/** @internal */ export async function initMetroCache() { const rawCache = await MMKVManager.getItem(BUNNY_METRO_CACHE_KEY); if (rawCache == null) return void buildInitCache(); @@ -72,6 +73,7 @@ function extractExportsFlags(moduleExports: any) { return bit; } +/** @internal */ export function indexExportsFlags(moduleId: number, moduleExports: any) { const flags = extractExportsFlags(moduleExports); if (flags && flags !== ModuleFlags.EXISTS) { @@ -79,10 +81,12 @@ export function indexExportsFlags(moduleId: number, moduleExports: any) { } } +/** @internal */ export function indexBlacklistFlag(id: number) { _metroCache.exportsIndex[id] |= ModuleFlags.BLACKLISTED; } +/** @internal */ export function getCacherForUniq(uniq: string, allFind: boolean) { const indexObject = _metroCache.findIndex[uniq] ??= {}; @@ -102,12 +106,13 @@ export function getCacherForUniq(uniq: string, allFind: boolean) { }; } +/** @internal */ export function getPolyfillModuleCacher(name: string) { const indexObject = _metroCache.polyfillIndex[name] ??= {}; return { getModules() { - return require("@metro/modules").getCachedPolyfillModules(name); + return require("@metro/internals/modules").getCachedPolyfillModules(name); }, cacheId(moduleId: number) { indexObject[moduleId] = 1; @@ -120,6 +125,7 @@ export function getPolyfillModuleCacher(name: string) { }; } +/** @internal */ export function indexAssetName(name: string, moduleId: number) { if (!isNaN(moduleId)) { (_metroCache.assetsIndex[name] ??= {})[moduleId] = 1; diff --git a/src/metro/enums.ts b/src/metro/internals/enums.ts similarity index 100% rename from src/metro/enums.ts rename to src/metro/internals/enums.ts diff --git a/src/metro/modules.ts b/src/metro/internals/modules.ts similarity index 98% rename from src/metro/modules.ts rename to src/metro/internals/modules.ts index 35db26e..cbfc1a9 100644 --- a/src/metro/modules.ts +++ b/src/metro/internals/modules.ts @@ -1,4 +1,4 @@ -import { getMetroCache, indexBlacklistFlag, indexExportsFlags } from "@metro/caches"; +import { getMetroCache, indexBlacklistFlag, indexExportsFlags } from "@metro/internals/caches"; import { Metro } from "@metro/types"; import { ModuleFlags, ModulesMapInternal } from "./enums"; @@ -105,7 +105,7 @@ function onModuleRequire(moduleExports: any, id: Metro.ModuleID) { get(target, property, receiver) { if (property === "isDeveloper") { // Hopefully won't explode accessing it here :3 - const { settings } = require("@lib/settings"); + const { settings } = require("@lib/api/settings"); return settings.enableDiscordDeveloperSettings ?? false; } diff --git a/src/metro/lazy.ts b/src/metro/lazy.ts index 16fb866..0ada336 100644 --- a/src/metro/lazy.ts +++ b/src/metro/lazy.ts @@ -1,9 +1,9 @@ import { _patcherDelaySymbol } from "@lib/api/patcher"; import { proxyLazy } from "@lib/utils/lazy"; -import { getMetroCache } from "./caches"; import { findExports } from "./finders"; -import { metroModules, subscribeModule } from "./modules"; +import { getMetroCache } from "./internals/caches"; +import { metroModules, subscribeModule } from "./internals/modules"; import type { FilterFn, LazyModuleContext } from "./types"; /** @internal */ diff --git a/src/metro/polyfills/redesign.ts b/src/metro/polyfills/redesign.ts index d7ca745..9e5c1a8 100644 --- a/src/metro/polyfills/redesign.ts +++ b/src/metro/polyfills/redesign.ts @@ -1,4 +1,4 @@ -import { getPolyfillModuleCacher } from "@metro/caches"; +import { getPolyfillModuleCacher } from "@metro/internals/caches"; import { LiteralUnion } from "type-fest"; const redesignProps = new Set([ diff --git a/src/metro/utils.ts b/src/metro/wrappers.ts similarity index 55% rename from src/metro/utils.ts rename to src/metro/wrappers.ts index ff3e107..ede0915 100644 --- a/src/metro/utils.ts +++ b/src/metro/wrappers.ts @@ -1,45 +1,6 @@ - -import { byDisplayName, byFilePath, byName, byProps, byStoreName, byTypeName } from "./filters"; -import { findAllExports, findExports } from "./finders"; +import { byDisplayName, byFilePath,byName, byProps, byStoreName, byTypeName } from "./filters"; +import { findAllExports,findExports } from "./finders"; import { createLazyModule } from "./lazy"; -import { FilterCheckDef, FilterDefinition, ModuleExports } from "./types"; - -export function createFilterDefinition( - fn: FilterCheckDef, - uniqMaker: (args: A) => string -): FilterDefinition { - function createHolder(func: T, args: A, raw: boolean) { - return Object.assign(func, { - filter: fn, - raw, - uniq: [ - raw && "raw::", - uniqMaker(args) - ].filter(Boolean).join("") - }); - } - - const curry = (raw: boolean) => (...args: A) => { - return createHolder((m: ModuleExports, id: number, defaultCheck: boolean) => { - return fn(args, m, id, defaultCheck); - }, args, raw); - }; - - return Object.assign(curry(false), { - byRaw: curry(true), - uniqMaker - }); -} - -export function createSimpleFilter( - filter: (m: ModuleExports) => boolean, - uniq: string -) { - return createFilterDefinition( - (_, m) => filter(m), - () => `dynamic::${uniq}` - )(); -} export const findByProps = (...props: string[]) => findExports(byProps(...props)); export const findByPropsLazy = (...props: string[]) => createLazyModule(byProps(...props)); @@ -60,6 +21,5 @@ export const findByTypeNameAll = (name: string, expDefault = true) => findAllExp export const findByStoreName = (name: string) => findExports(byStoreName(name)); export const findByStoreNameLazy = (name: string) => createLazyModule(byStoreName(name)); -export const findByFilePath = (path: string) => findExports(byFilePath(path)); -export const findByFilePathLazy = (path: string) => createLazyModule(byFilePath(path)); - +export const findByFilePath = (path: string, expDefault = false) => findExports(byFilePath(path, expDefault)); +export const findByFilePathLazy = (path: string, expDefault = false) => createLazyModule(byFilePath(path, expDefault));