diff --git a/lib/plugins/src/_internal.ts b/lib/plugins/src/_internal.ts index 32d6c44..c31b445 100644 --- a/lib/plugins/src/_internal.ts +++ b/lib/plugins/src/_internal.ts @@ -1,19 +1,12 @@ import { TypedEventEmitter } from '@revenge-mod/discord/common/utils' -import { getStorage } from '@revenge-mod/storage' -import { defineLazyProperty } from '@revenge-mod/utils/objects' import { allSettled, sleepReject } from '@revenge-mod/utils/promises' import { _uapi as uapi } from './apis' -import { - PluginFlags as Flag, - PluginsStorageDirectory, - PluginStatus as Status, -} from './constants' -import type { StorageOptions } from '@revenge-mod/storage' -import type { AnyObject } from '@revenge-mod/utils/types' +import { PluginFlags as Flag, PluginStatus as Status } from './constants' import type { InitPluginApi, Plugin, PluginApi, + PluginApiExtensionOptions, PluginCleanup, PluginManifest, PluginOptions, @@ -23,7 +16,7 @@ import type { export const _uapi = uapi export const _emitter = new TypedEventEmitter<{ - register: [InternalPlugin] + register: [InternalPlugin, PluginOptions] disabled: [InternalPlugin] enabled: [InternalPlugin] // Add preInit API extensions @@ -50,9 +43,9 @@ export const _metas = new Map< ] >() -export function registerPlugin( +export function registerPlugin( manifest: PluginManifest, - options: PluginOptions, + options: PluginOptions, flags: number, iflags: number, ) { @@ -62,7 +55,6 @@ export function registerPlugin( const plugin: InternalPlugin = { _c: [], - _s: options.storage, errors: [], manifest, lifecycles: { @@ -81,7 +73,7 @@ export function registerPlugin( _metas.set(manifest.id, [undefined, [], iflags, PluginApiLevel.None]) _plugins.set(manifest.id, plugin) - _emitter.emit('register', plugin) + _emitter.emit('register', plugin, options) } function handlePluginError(e: unknown, plugin: InternalPlugin) { @@ -118,26 +110,15 @@ function preparePluginPreInit(plugin: InternalPlugin) { function preparePluginInit(plugin: InternalPlugin) { const meta = _metas.get(plugin.manifest.id)! - const api = meta[0] as InitPluginApi - defineLazyProperty(api, 'storage', () => - getStorage(`${PluginsStorageDirectory}/${plugin.manifest.id}.json`, { - ...plugin._s, - directory: 'documents', - }), - ) - - _emitter.emit('init', plugin, api) + _emitter.emit('init', plugin, meta[0] as InitPluginApi) meta[3] = PluginApiLevel.Init } function preparePluginStart(plugin: InternalPlugin) { const meta = _metas.get(plugin.manifest.id)! - const api = meta[0] as PluginApi - // api.settings = ... - - _emitter.emit('start', plugin, api) + _emitter.emit('start', plugin, meta[0] as PluginApi) meta[3] = PluginApiLevel.Start } @@ -381,8 +362,7 @@ export async function stopPlugin(plugin: InternalPlugin) { } } -export interface InternalPlugin extends Plugin { - _s?: StorageOptions +export interface InternalPlugin extends Plugin { _c: PluginCleanup[] } diff --git a/lib/plugins/src/init.ts b/lib/plugins/src/init.ts index bfe4a19..528d3d5 100644 --- a/lib/plugins/src/init.ts +++ b/lib/plugins/src/init.ts @@ -17,10 +17,6 @@ for (const plugin of _plugins.values()) const unsub = onRunApplication(() => { unsub() - // TODO(plugins/start): init start APIs - // const UILibrary = require('@revenge-mod/ui') - // uapi.ui = UiLibrary - for (const plugin of _plugins.values()) if (plugin.flags & PluginFlags.Enabled) startPlugin(plugin) }) diff --git a/lib/plugins/src/types.ts b/lib/plugins/src/types.ts index 097b856..be5ee2e 100644 --- a/lib/plugins/src/types.ts +++ b/lib/plugins/src/types.ts @@ -1,16 +1,19 @@ -import type { Storage, StorageOptions } from '@revenge-mod/storage' -import type { AnyObject } from '@revenge-mod/utils/types' import type { FunctionComponent } from 'react' import type { PluginApiModules } from './apis/modules' import type { PluginApiPlugins } from './apis/plugins' import type { PluginApiReact } from './apis/react' import type { PluginFlags, PluginStatus } from './constants' +// biome-ignore lint/suspicious/noEmptyInterface: To be extended by actual extensions +export interface PluginApiExtensionOptions {} + /** * The unscoped plugin API (very limited). This API is available as a global for plugins. * Available in the `preInit` phase. */ -export interface UnscopedPreInitPluginApi { +export interface UnscopedPreInitPluginApi< + O extends PluginApiExtensionOptions = PluginApiExtensionOptions, +> { modules: PluginApiModules patcher: typeof import('@revenge-mod/patcher') plugins: PluginApiPlugins @@ -21,7 +24,9 @@ export interface UnscopedPreInitPluginApi { * The unscoped plugin API (limited). This API is available as a global for plugins. * Available in the `init` phase. */ -export interface UnscopedInitPluginApi extends UnscopedPreInitPluginApi { +export interface UnscopedInitPluginApi< + O extends PluginApiExtensionOptions = PluginApiExtensionOptions, +> extends UnscopedPreInitPluginApi { assets: typeof import('@revenge-mod/assets') react: PluginApiReact } @@ -30,7 +35,9 @@ export interface UnscopedInitPluginApi extends UnscopedPreInitPluginApi { * The unscoped plugin API. This API is available as a global for plugins. * Available in the `start` and `stop` phase. */ -export interface UnscopedPluginApi extends UnscopedInitPluginApi { +export interface UnscopedPluginApi< + O extends PluginApiExtensionOptions = PluginApiExtensionOptions, +> extends UnscopedInitPluginApi { // ui: typeof import('@revenge-mod/ui') } @@ -41,7 +48,9 @@ export type PluginCleanupApi = (...fns: PluginCleanup[]) => void * The plugin API (very limited). * Available in the `preInit` phase. */ -export interface PreInitPluginApi { +export interface PreInitPluginApi< + O extends PluginApiExtensionOptions = PluginApiExtensionOptions, +> { unscoped: UnscopedPreInitPluginApi cleanup: PluginCleanupApi plugin: Plugin @@ -51,26 +60,19 @@ export interface PreInitPluginApi { * The plugin API (limited). * Available in the `init` phase. */ -export interface InitPluginApi - extends PreInitPluginApi { +export interface InitPluginApi< + O extends PluginApiExtensionOptions = PluginApiExtensionOptions, +> extends PreInitPluginApi { unscoped: UnscopedInitPluginApi - /** - * The plugin storage. - * - * Note that the instance is only created when the plugin accesses the API. - * This is to prevent unnecessary storage instances from being created. - * - * To preload storage, simply call `api.storage.get()`. - */ - storage: Storage } /** * The plugin API. * Available in the `start` and `stop` phase. */ -export interface PluginApi - extends InitPluginApi { +export interface PluginApi< + O extends PluginApiExtensionOptions = PluginApiExtensionOptions, +> extends InitPluginApi { unscoped: UnscopedPluginApi } @@ -113,42 +115,46 @@ export interface PluginManifest { icon?: string } -export interface PluginOptions - extends PluginLifecycles { - SettingsComponent?: PluginSettingsComponent - storage?: Omit, 'directory'> +export interface PluginOptions< + O extends PluginApiExtensionOptions = PluginApiExtensionOptions, +> extends PluginLifecycles { + SettingsComponent?: PluginSettingsComponent } -export interface PluginLifecycles { +export interface PluginLifecycles< + O extends PluginApiExtensionOptions = PluginApiExtensionOptions, +> { /** * Runs as soon as possible with very limited APIs. * Before the index module (module 0)'s factory is run. * * @param api Plugin API (very limited). */ - preInit?: (api: PreInitPluginApi) => any + preInit?: (api: PreInitPluginApi) => any /** * Runs as soon as all important modules are initialized. * After the index module (module 0)'s factory is run. * * @param api Plugin API (limited). */ - init?: (api: InitPluginApi) => any + init?: (api: InitPluginApi) => any /** * Runs when the plugin can be started with all APIs available. * * @param api Plugin API. */ - start?: (api: PluginApi) => any + start?: (api: PluginApi) => any /** * Runs when the plugin is stopped. * * @param api Plugin API. */ - stop?: (api: PluginApi) => any + stop?: (api: PluginApi) => any } -export interface Plugin { +export interface Plugin< + O extends PluginApiExtensionOptions = PluginApiExtensionOptions, +> { // TODO(plugins): support plugin bundles // /** // * The plugin bundle this plugin belongs to. @@ -161,7 +167,7 @@ export interface Plugin { /** * The plugin lifecycles. */ - lifecycles: PluginLifecycles + lifecycles: PluginLifecycles /** * The plugin flags. @@ -181,7 +187,7 @@ export interface Plugin { /** * The plugin settings page. */ - SettingsComponent?: PluginSettingsComponent + SettingsComponent?: PluginSettingsComponent /** * Disable the plugin. This will also stop the plugin if it is running. @@ -193,5 +199,6 @@ export interface Plugin { stop(): Promise } -export type PluginSettingsComponent = - FunctionComponent<{ api: PluginApi }> +export interface PluginSettingsComponent< + O extends PluginApiExtensionOptions = PluginApiExtensionOptions, +> extends FunctionComponent<{ api: PluginApi }> {} diff --git a/lib/storage/package.json b/lib/storage/package.json index f7f6427..0d1165a 100644 --- a/lib/storage/package.json +++ b/lib/storage/package.json @@ -5,6 +5,12 @@ "exports": { ".": { "default": "./src/index.ts" + }, + "./preinit": { + "default": "./src/preinit.ts" + }, + "./init": { + "default": "./src/init.ts" } } } diff --git a/lib/storage/src/index.ts b/lib/storage/src/index.ts index 6c9b307..c0dda0d 100644 --- a/lib/storage/src/index.ts +++ b/lib/storage/src/index.ts @@ -108,7 +108,7 @@ export function getStorage( return storage } -export interface StorageOptions { +export interface StorageOptions { /** * The directory of the storage file. */ diff --git a/lib/storage/src/init.ts b/lib/storage/src/init.ts new file mode 100644 index 0000000..d0d447a --- /dev/null +++ b/lib/storage/src/init.ts @@ -0,0 +1,13 @@ +import { _emitter } from '@revenge-mod/plugins/_' +import { PluginsStorageDirectory } from '@revenge-mod/plugins/constants' +import { defineLazyProperty } from '@revenge-mod/utils/objects' +import { getStorage } from '.' + +_emitter.on('init', ({ _s: storageOptions, manifest: { id } }, api) => + defineLazyProperty(api, 'storage', () => + getStorage(`${PluginsStorageDirectory}/${id}.json`, { + ...storageOptions, + directory: 'documents', + }), + ), +) diff --git a/lib/storage/src/preinit.ts b/lib/storage/src/preinit.ts new file mode 100644 index 0000000..653e3a0 --- /dev/null +++ b/lib/storage/src/preinit.ts @@ -0,0 +1,3 @@ +import { _emitter } from '@revenge-mod/plugins/_' + +_emitter.on('register', (plugin, options) => (plugin._s = options.storage)) diff --git a/lib/storage/src/types.ts b/lib/storage/src/types.ts new file mode 100644 index 0000000..ddde34a --- /dev/null +++ b/lib/storage/src/types.ts @@ -0,0 +1,42 @@ +import type { + PluginApiExtensionOptions, + PluginManifest, + PluginOptions, +} from '@revenge-mod/plugins/types' +import type { AnyObject } from '@revenge-mod/utils/types' +import type { Storage, StorageOptions } from '.' + +declare module '@revenge-mod/plugins/types' { + export interface PluginApiExtensionOptions { + storage: AnyObject + } + + export interface PluginOptions { + storage?: Omit, 'directory'> + } + + export interface InitPluginApi { + /** + * The plugin storage. + * + * Note that the instance is only created when the plugin accesses the API. + * This is to prevent unnecessary storage instances from being created. + * + * To preload storage, simply call `api.storage.get()`. + */ + storage: Storage + } +} + +declare module '@revenge-mod/plugins/_' { + export function registerPlugin( + manifest: PluginManifest, + options: PluginOptions, + flags: number, + iflags: number, + ): void + + export interface InternalPlugin { + _s?: StorageOptions + } +} diff --git a/src/index.ts b/src/index.ts index 20e45d7..f548afc 100644 --- a/src/index.ts +++ b/src/index.ts @@ -26,6 +26,7 @@ onModuleFirstRequired(IndexModuleId, function onIndexRequired() { require('@revenge-mod/modules/preinit') require('@revenge-mod/react/preinit') require('@revenge-mod/assets/preinit') + require('@revenge-mod/storage/preinit') require('@revenge-mod/discord/preinit') // Run all preinit plugins @@ -35,6 +36,7 @@ onModuleFirstRequired(IndexModuleId, function onIndexRequired() { onModuleInitialized(IndexModuleId, function onIndexInitialized() { runCatching(() => { // Initialize init libraries + require('@revenge-mod/storage/init') require('@revenge-mod/externals/init') require('@revenge-mod/discord/init') require('@revenge-mod/components/init') diff --git a/src/plugins/init/developer-settings/index.ts b/src/plugins/init/developer-settings/index.ts index c0e04ca..036f15d 100644 --- a/src/plugins/init/developer-settings/index.ts +++ b/src/plugins/init/developer-settings/index.ts @@ -21,7 +21,7 @@ const defaultStorage: Storage = { }, } -registerPlugin( +registerPlugin<{ storage: Storage }>( { id: 'revenge.settings.developer', name: 'Developer Settings', @@ -57,4 +57,4 @@ registerPlugin( ) // Expose to EvaluateJavaScriptSetting -export let api: PluginApi +export let api: PluginApi<{ storage: Storage }>