fix(lib/plugins/apis): set most APIs during pre-init phase

This commit is contained in:
PalmDevs 2026-04-07 22:01:49 +07:00
parent 49fe4232c7
commit 5c892bb8a1
No known key found for this signature in database
GPG key ID: 6FAE8F8677ACA8BA
15 changed files with 194 additions and 241 deletions

View file

@ -48,9 +48,6 @@
"./types": {
"types": "./src/types/index.ts"
},
"./types/api": {
"types": "./src/types/api.ts"
},
"./_/modules/settings": {
"default": "./src/modules/settings/_internal.ts"
}

View file

@ -1,41 +1,6 @@
import type { DiscordModules } from '.'
export interface PluginApiDiscord {
actions: PluginApiDiscord.Actions
common: PluginApiDiscord.Common
design: PluginApiDiscord.Design
flux: PluginApiDiscord.Flux
modules: PluginApiDiscord.Modules
native: PluginApiDiscord.Native
utils: PluginApiDiscord.Utils
}
export namespace PluginApiDiscord {
export type Actions = typeof import('@revenge-mod/discord/actions')
export type Common = typeof import('@revenge-mod/discord/common')
export type Design = typeof import('@revenge-mod/discord/design')
export type Flux = typeof import('@revenge-mod/discord/flux')
export type Native = typeof import('@revenge-mod/discord/native')
export interface Utils {
finders: typeof import('@revenge-mod/discord/utils/modules/finders')
metro: {
subscriptions: typeof import('@revenge-mod/discord/utils/modules/metro/subscriptions')
}
}
export interface Modules {
mainTabsV2: typeof import('@revenge-mod/discord/modules/main_tabs_v2')
settings: typeof import('@revenge-mod/discord/modules/settings') &
typeof import('@revenge-mod/discord/modules/settings/renderer')
}
}
declare module '@revenge-mod/plugins/types' {
export interface UnscopedInitPluginApi {
discord: PluginApiDiscord
}
export interface InitPluginApi {
logger: DiscordModules.Logger
}

View file

@ -17,9 +17,6 @@
},
"./shopify": {
"default": "./src/shopify.ts"
},
"./types": {
"types": "./src/types.ts"
}
}
}

View file

@ -1,13 +0,0 @@
export interface PluginApiExternals {
Browserify: typeof import('@revenge-mod/externals/browserify')
ReactNativeClipboard: typeof import('@revenge-mod/externals/react-native-clipboard')
ReactNativeSafeAreaContext: typeof import('@revenge-mod/externals/react-native-safe-area-context')
ReactNavigation: typeof import('@revenge-mod/externals/react-navigation')
Shopify: typeof import('@revenge-mod/externals/shopify')
}
declare module '@revenge-mod/plugins/types' {
export interface UnscopedInitPluginApi {
externals: PluginApiExternals
}
}

View file

@ -0,0 +1,85 @@
import defer * as DiscordActions from '@revenge-mod/discord/actions'
import defer * as DiscordCommon from '@revenge-mod/discord/common'
import defer * as DiscordDesign from '@revenge-mod/discord/design'
import defer * as DiscordFlux from '@revenge-mod/discord/flux'
import defer * as DiscordModulesMainTabsV2 from '@revenge-mod/discord/modules/main_tabs_v2'
import defer * as DiscordNative from '@revenge-mod/discord/native'
import defer * as DiscordUtilsFinders from '@revenge-mod/discord/utils/modules/finders'
import defer * as DiscordUtilsMetroSubscriptions from '@revenge-mod/discord/utils/modules/metro/subscriptions'
import { defineLazyProperties } from '@revenge-mod/utils/object'
import { guardIndexInitialized } from '.'
export interface PluginApiDiscord {
actions: PluginApiDiscord.Actions
/**
* This submodule is only available after the index module (ID 0) is initialized.
* Attempting to access it before then will throw an error.
*/
common: PluginApiDiscord.Common
design: PluginApiDiscord.Design
flux: PluginApiDiscord.Flux
modules: PluginApiDiscord.Modules
native: PluginApiDiscord.Native
utils: PluginApiDiscord.Utils
}
export namespace PluginApiDiscord {
export type Actions = typeof import('@revenge-mod/discord/actions')
export type Common = typeof import('@revenge-mod/discord/common')
export type Design = typeof import('@revenge-mod/discord/design')
export type Flux = typeof import('@revenge-mod/discord/flux')
export type Native = typeof import('@revenge-mod/discord/native')
export interface Utils {
finders: typeof import('@revenge-mod/discord/utils/modules/finders')
metro: {
subscriptions: typeof import('@revenge-mod/discord/utils/modules/metro/subscriptions')
}
}
export interface Modules {
mainTabsV2: typeof import('@revenge-mod/discord/modules/main_tabs_v2')
settings: typeof import('@revenge-mod/discord/modules/settings') &
typeof import('@revenge-mod/discord/modules/settings/renderer')
}
}
export const discord = defineLazyProperties(
{
modules: defineLazyProperties({} as PluginApiDiscord.Modules, {
mainTabsV2: () => {
return DiscordModulesMainTabsV2
},
settings: () => ({
...require('@revenge-mod/discord/modules/settings'),
...require('@revenge-mod/discord/modules/settings/renderer'),
}),
}),
utils: defineLazyProperties({} as PluginApiDiscord.Utils, {
finders: () => {
return DiscordUtilsFinders
},
metro: () => ({
subscriptions: DiscordUtilsMetroSubscriptions,
}),
}),
} as PluginApiDiscord,
{
actions: () => {
return DiscordActions
},
common: () => {
guardIndexInitialized('Discord.common')
return DiscordCommon
},
flux: () => {
return DiscordFlux
},
design: () => {
return DiscordDesign
},
native: () => {
return DiscordNative
},
},
)

View file

@ -0,0 +1,35 @@
import defer * as Browserify from '@revenge-mod/externals/browserify'
import defer * as ReactNativeClipboard from '@revenge-mod/externals/react-native-clipboard'
import defer * as ReactNativeSafeAreaContext from '@revenge-mod/externals/react-native-safe-area-context'
import defer * as ReactNavigation from '@revenge-mod/externals/react-navigation'
import defer * as Shopify from '@revenge-mod/externals/shopify'
import { defineLazyProperties } from '@revenge-mod/utils/object'
export interface PluginApiExternals {
Browserify: typeof import('@revenge-mod/externals/browserify')
ReactNativeClipboard: typeof import('@revenge-mod/externals/react-native-clipboard')
ReactNativeSafeAreaContext: typeof import('@revenge-mod/externals/react-native-safe-area-context')
ReactNavigation: typeof import('@revenge-mod/externals/react-navigation')
Shopify: typeof import('@revenge-mod/externals/shopify')
}
export const externals: PluginApiExternals = defineLazyProperties(
{} as PluginApiExternals,
{
Browserify: () => {
return Browserify
},
ReactNativeClipboard: () => {
return ReactNativeClipboard
},
ReactNativeSafeAreaContext: () => {
return ReactNativeSafeAreaContext
},
ReactNavigation: () => {
return ReactNavigation
},
Shopify: () => {
return Shopify
},
},
)

View file

@ -1,4 +1,10 @@
import * as assets from '@revenge-mod/assets'
import defer * as Components from '@revenge-mod/components'
import { isModuleInitialized } from '@revenge-mod/modules/metro/utils'
import * as patcher from '@revenge-mod/patcher'
import { defineLazyProperties } from '@revenge-mod/utils/object'
import defer * as Discord from './discord'
import { externals } from './externals'
import { modules } from './modules'
import { plugins } from './plugins'
import { react } from './react'
@ -17,6 +23,28 @@ export const pUnscopedApi:
patcher,
plugins,
react,
assets,
externals,
}
defineLazyProperties(pUnscopedApi, {
components: () => {
guardIndexInitialized('Components')
return Components
},
})
defineLazyProperties(pUnscopedApi, {
discord: () => {
return Discord.discord
},
})
export function guardIndexInitialized(name: string) {
if (!isModuleInitialized(0))
throw new Error(
`Cannot access ${name} API before index module (ID 0) is initialized`,
)
}
export function spreadDescriptors<T extends object, U extends object>(

View file

@ -1,4 +1,6 @@
import type { FunctionComponent } from 'react'
import type { PluginApiDiscord } from './apis/discord'
import type { PluginApiExternals } from './apis/externals'
import type { PluginApiModules } from './apis/modules'
import type { PluginApiPlugins } from './apis/plugins'
import type { PluginApiReact } from './apis/react'
@ -19,6 +21,10 @@ export interface UnscopedPreInitPluginApi<
patcher: typeof import('@revenge-mod/patcher')
plugins: PluginApiPlugins
react: PluginApiReact
assets: typeof import('@revenge-mod/assets')
externals: PluginApiExternals
components: typeof import('@revenge-mod/components')
discord: PluginApiDiscord
}
/**
@ -27,9 +33,7 @@ export interface UnscopedPreInitPluginApi<
*/
export interface UnscopedInitPluginApi<
O extends PluginApiExtensionsOptions = PluginApiExtensionsOptions,
> extends UnscopedPreInitPluginApi<O> {
assets: typeof import('@revenge-mod/assets')
}
> extends UnscopedPreInitPluginApi<O> {}
/**
* The unscoped plugin API. This API is available as a global for plugins.
@ -112,7 +116,6 @@ export interface PreInitPluginApi<
* The plugin API (limited).
* Available in the `init` phase.
*/
// @ts-expect-error
export interface InitPluginApi<
O extends PluginApiExtensionsOptions = PluginApiExtensionsOptions,
> extends PreInitPluginApi<O> {
@ -125,7 +128,6 @@ export interface InitPluginApi<
* The plugin API.
* Available in the `start` and `stop` phase.
*/
// @ts-expect-error
export interface PluginApi<
O extends PluginApiExtensionsOptions = PluginApiExtensionsOptions,
> extends InitPluginApi<O> {

View file

@ -28,10 +28,10 @@ export interface PreInitPluginApiUtils {
promise: typeof import('@revenge-mod/utils/promise')
proxy: typeof import('@revenge-mod/utils/proxy')
tree: typeof import('@revenge-mod/utils/tree')
react: typeof import('@revenge-mod/utils/react')
}
export interface PluginApiUtils extends PreInitPluginApiUtils {
react: typeof import('@revenge-mod/utils/react')
discord: typeof import('@revenge-mod/utils/discord')
}

View file

@ -1,23 +0,0 @@
import * as assets from '@revenge-mod/assets'
import { InternalPluginFlags, registerPlugin } from '@revenge-mod/plugins/_'
import { PluginFlags } from '@revenge-mod/plugins/constants'
registerPlugin(
{
id: 'revenge.api.assets',
name: 'Assets API',
description: '@revenge-mod/assets API for plugins.',
author: 'Revenge',
icon: 'PollsIcon',
},
{
init({ unscoped }) {
unscoped.assets = assets
},
},
PluginFlags.Enabled,
// biome-ignore format: Don't format this
InternalPluginFlags.Internal |
InternalPluginFlags.Essential |
InternalPluginFlags.API,
)

View file

@ -1,28 +0,0 @@
import defer * as Components from '@revenge-mod/components'
import { InternalPluginFlags, registerPlugin } from '@revenge-mod/plugins/_'
import { PluginFlags } from '@revenge-mod/plugins/constants'
import { defineLazyProperties } from '@revenge-mod/utils/object'
registerPlugin(
{
id: 'revenge.api.components',
name: 'Components API',
description: '@revenge-mod/components API for plugins.',
author: 'Revenge',
icon: 'PollsIcon',
},
{
init({ unscoped }) {
defineLazyProperties(unscoped, {
components: () => {
return Components
},
})
},
},
PluginFlags.Enabled,
// biome-ignore format: Don't format this
InternalPluginFlags.Internal |
InternalPluginFlags.Essential |
InternalPluginFlags.API,
)

View file

@ -1,81 +0,0 @@
import defer * as DiscordActions from '@revenge-mod/discord/actions'
import defer * as DiscordCommon from '@revenge-mod/discord/common'
import { Logger } from '@revenge-mod/discord/common'
import defer * as DiscordDesign from '@revenge-mod/discord/design'
import defer * as DiscordFlux from '@revenge-mod/discord/flux'
import defer * as DiscordModulesMainTabsV2 from '@revenge-mod/discord/modules/main_tabs_v2'
import defer * as DiscordNative from '@revenge-mod/discord/native'
import { InternalPluginFlags, registerPlugin } from '@revenge-mod/plugins/_'
import { PluginFlags } from '@revenge-mod/plugins/constants'
import {
defineLazyProperties,
defineLazyProperty,
} from '@revenge-mod/utils/object'
import type { PluginApiDiscord } from '@revenge-mod/discord/types/api'
registerPlugin(
{
id: 'revenge.api.discord',
name: 'Discord API',
description: '@revenge-mod/discord API for plugins.',
author: 'Revenge',
icon: 'PollsIcon',
},
{
init({ unscoped, decorate }) {
decorate(plugin => {
defineLazyProperty(
plugin.api,
'logger',
() =>
new Logger(`Revenge > Plugins (${plugin.manifest.id})`),
)
})
defineLazyProperties(
(unscoped.discord = {
modules: defineLazyProperties(
{} as PluginApiDiscord.Modules,
{
mainTabsV2: () => {
return DiscordModulesMainTabsV2
},
settings: () => ({
...require('@revenge-mod/discord/modules/settings'),
...require('@revenge-mod/discord/modules/settings/renderer'),
}),
},
),
utils: {
finders: require('@revenge-mod/discord/utils/modules/finders'),
metro: {
subscriptions: require('@revenge-mod/discord/utils/modules/metro/subscriptions'),
},
},
} as PluginApiDiscord),
{
actions: () => {
return DiscordActions
},
common: () => {
return DiscordCommon
},
flux: () => {
return DiscordFlux
},
design: () => {
return DiscordDesign
},
native: () => {
return DiscordNative
},
},
)
},
},
PluginFlags.Enabled,
// biome-ignore format: Don't format this
InternalPluginFlags.Internal |
InternalPluginFlags.Essential |
InternalPluginFlags.API,
)

View file

@ -1,48 +0,0 @@
import defer * as Browserify from '@revenge-mod/externals/browserify'
import defer * as ReactNativeClipboard from '@revenge-mod/externals/react-native-clipboard'
import defer * as ReactNativeSafeAreaContext from '@revenge-mod/externals/react-native-safe-area-context'
import defer * as ReactNavigation from '@revenge-mod/externals/react-navigation'
import defer * as Shopify from '@revenge-mod/externals/shopify'
import { InternalPluginFlags, registerPlugin } from '@revenge-mod/plugins/_'
import { PluginFlags } from '@revenge-mod/plugins/constants'
import { defineLazyProperties } from '@revenge-mod/utils/object'
import type { PluginApiExternals } from '@revenge-mod/externals/types'
registerPlugin(
{
id: 'revenge.api.externals',
name: 'Externals API',
description: '@revenge-mod/externals API for plugins.',
author: 'Revenge',
icon: 'PollsIcon',
},
{
init({ unscoped }) {
defineLazyProperties(
(unscoped.externals = {} as PluginApiExternals),
{
Browserify: () => {
return Browserify
},
ReactNativeClipboard: () => {
return ReactNativeClipboard
},
ReactNativeSafeAreaContext: () => {
return ReactNativeSafeAreaContext
},
ReactNavigation: () => {
return ReactNavigation
},
Shopify: () => {
return Shopify
},
},
)
},
},
PluginFlags.Enabled,
// biome-ignore format: Don't format this
InternalPluginFlags.Internal |
InternalPluginFlags.Essential |
InternalPluginFlags.API,
)

View file

@ -0,0 +1,37 @@
import { InternalPluginFlags, registerPlugin } from '@revenge-mod/plugins/_'
import { PluginFlags } from '@revenge-mod/plugins/constants'
import { defineLazyProperty } from '@revenge-mod/utils/object'
registerPlugin(
{
id: 'revenge.api.discord',
name: 'Discord API',
description: '@revenge-mod/discord API for plugins.',
author: 'Revenge',
icon: 'PollsIcon',
},
{
init({
decorate,
unscoped: {
discord: {
common: { Logger },
},
},
}) {
decorate(plugin => {
defineLazyProperty(
plugin.api,
'logger',
() =>
new Logger(`Revenge > Plugins (${plugin.manifest.id})`),
)
})
},
},
PluginFlags.Enabled,
// biome-ignore format: Don't format this
InternalPluginFlags.Internal |
InternalPluginFlags.Essential |
InternalPluginFlags.API,
)

View file

@ -26,11 +26,11 @@ registerPlugin(
promise: UtilsPromise,
proxy: UtilsProxy,
tree: UtilsTree,
react: UtilsReact,
}
},
init({ unscoped: { utils } }) {
utils.discord = UtilsDiscord
utils.react = UtilsReact
},
},
PluginFlags.Enabled,