feat(lib/plugins): add API for storages
This commit is contained in:
parent
3c23236273
commit
7a1ff7ef0c
3 changed files with 74 additions and 21 deletions
|
|
@ -1,16 +1,18 @@
|
|||
import { getStorage, type StorageOptions } from '@revenge-mod/storage'
|
||||
import { allSettled, sleepReject } from '@revenge-mod/utils/promises'
|
||||
|
||||
import { _uapi } from './apis'
|
||||
|
||||
import { PluginFlags as Flag, PluginStatus as Status } from './constants'
|
||||
import { PluginFlags as Flag, PluginsStorageDirectory, PluginStatus as Status } from './constants'
|
||||
|
||||
import type { AnyObject } from '@revenge-mod/utils/types'
|
||||
import type {
|
||||
InitPluginApi,
|
||||
Plugin,
|
||||
PluginApi,
|
||||
PluginCleanup,
|
||||
PluginLifecycles,
|
||||
PluginManifest,
|
||||
PluginOptions,
|
||||
PreInitPluginApi,
|
||||
} from './types'
|
||||
|
||||
|
|
@ -25,15 +27,26 @@ export const _metas = new Map<
|
|||
]
|
||||
>()
|
||||
|
||||
export function registerPlugin(manifest: PluginManifest, lifecycles: PluginLifecycles, flags: number, iflags: number) {
|
||||
export function registerPlugin<S extends AnyObject = AnyObject>(
|
||||
manifest: PluginManifest,
|
||||
options: PluginOptions<S>,
|
||||
flags: number,
|
||||
iflags: number,
|
||||
) {
|
||||
// TODO(plugins): verify plugin manifest
|
||||
if (_plugins.has(manifest.id)) throw new Error(`Plugin with ID "${manifest.id}" already registered`)
|
||||
|
||||
const plugin: InternalPlugin = {
|
||||
cleanups: [],
|
||||
_c: [],
|
||||
_s: options.storage,
|
||||
errors: [],
|
||||
manifest,
|
||||
lifecycles,
|
||||
lifecycles: {
|
||||
preInit: options.preInit,
|
||||
init: options.init,
|
||||
start: options.start,
|
||||
stop: options.stop,
|
||||
},
|
||||
status: 0,
|
||||
flags,
|
||||
disable: () => disablePlugin(plugin),
|
||||
|
|
@ -65,7 +78,7 @@ function preparePluginPreInit(id: PluginManifest['id']) {
|
|||
|
||||
meta[0] = {
|
||||
cleanup: (...items) => {
|
||||
plugin.cleanups.push(...items)
|
||||
plugin._c.push(...items)
|
||||
},
|
||||
plugin,
|
||||
unscoped: _uapi,
|
||||
|
|
@ -74,11 +87,33 @@ function preparePluginPreInit(id: PluginManifest['id']) {
|
|||
meta[3] = PluginApiLevel.PreInit
|
||||
}
|
||||
|
||||
function preparePluginInit(id: PluginManifest['id']) {
|
||||
function preparePluginInit(id: PluginManifest['id'], storageOptions?: StorageOptions<AnyObject>) {
|
||||
const meta = _metas.get(id)!
|
||||
const api = meta[0] as InitPluginApi
|
||||
|
||||
api.logger = new api.unscoped.discord.common.Logger(`Revenge > Plugins (${id})`)
|
||||
Object.defineProperties(api, {
|
||||
logger: {
|
||||
configurable: true,
|
||||
get() {
|
||||
// @ts-expect-error
|
||||
// biome-ignore lint/performance/noDelete: <explanation>
|
||||
delete api.logger
|
||||
return (api.logger = new api.unscoped.discord.common.Logger(`Revenge > Plugins (${id})`))
|
||||
},
|
||||
},
|
||||
storage: {
|
||||
configurable: true,
|
||||
get() {
|
||||
// @ts-expect-error
|
||||
// biome-ignore lint/performance/noDelete: <explanation>
|
||||
delete api.storage
|
||||
return (api.storage = getStorage(`${PluginsStorageDirectory}/${id}.json`, {
|
||||
...storageOptions,
|
||||
directory: 'documents',
|
||||
}))
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
meta[3] = PluginApiLevel.Init
|
||||
}
|
||||
|
|
@ -224,7 +259,7 @@ export async function stopPlugin(plugin: InternalPlugin) {
|
|||
throw new Error(`Plugin "${manifest.id}" is not running`)
|
||||
|
||||
if (apiLevel < PluginApiLevel.PreInit) preparePluginPreInit(manifest.id)
|
||||
if (apiLevel < PluginApiLevel.Init) preparePluginInit(manifest.id)
|
||||
if (apiLevel < PluginApiLevel.Init) preparePluginInit(manifest.id, plugin._s)
|
||||
if (apiLevel < PluginApiLevel.Start) preparePluginStart(manifest.id)
|
||||
|
||||
plugin.status |= Status.Stopping
|
||||
|
|
@ -239,7 +274,7 @@ export async function stopPlugin(plugin: InternalPlugin) {
|
|||
await handlePluginError(e, plugin)
|
||||
} finally {
|
||||
// Run cleanups
|
||||
const results = await allSettled(plugin.cleanups.map(cleanup => cleanup()))
|
||||
const results = await allSettled(plugin._c.map(cleanup => cleanup()))
|
||||
for (const result of results)
|
||||
if (result.status === 'rejected') {
|
||||
await handlePluginError(result.reason, plugin)
|
||||
|
|
@ -253,7 +288,7 @@ export async function stopPlugin(plugin: InternalPlugin) {
|
|||
|
||||
// Clear temp data
|
||||
meta[1] = []
|
||||
plugin.cleanups = []
|
||||
plugin._c = []
|
||||
|
||||
// Reset status
|
||||
plugin.status = 0
|
||||
|
|
@ -261,7 +296,8 @@ export async function stopPlugin(plugin: InternalPlugin) {
|
|||
}
|
||||
|
||||
export interface InternalPlugin extends Plugin {
|
||||
cleanups: PluginCleanup[]
|
||||
_s?: StorageOptions<AnyObject>
|
||||
_c: PluginCleanup[]
|
||||
}
|
||||
|
||||
export const InternalPluginFlags = {
|
||||
|
|
|
|||
|
|
@ -27,3 +27,5 @@ export const PluginStatus = {
|
|||
Started: 1 << 5,
|
||||
Stopping: 1 << 6,
|
||||
}
|
||||
|
||||
export const PluginsStorageDirectory = 'revenge/plugins/storage'
|
||||
|
|
|
|||
|
|
@ -2,6 +2,9 @@ import type { PluginFlags, PluginStatus } from './constants'
|
|||
|
||||
import type { DiscordModules } from '@revenge-mod/discord/types'
|
||||
|
||||
import type { Storage, StorageOptions } from '@revenge-mod/storage'
|
||||
import type { AnyObject } from '@revenge-mod/utils/types'
|
||||
|
||||
import type { PluginApiDiscord } from './apis/discord'
|
||||
import type { PluginApiExternals } from './apis/externals'
|
||||
import type { PluginApiModules } from './apis/modules'
|
||||
|
|
@ -55,17 +58,25 @@ export interface PreInitPluginApi {
|
|||
* The plugin API (limited).
|
||||
* Available in the `init` phase.
|
||||
*/
|
||||
export interface InitPluginApi extends PreInitPluginApi {
|
||||
export interface InitPluginApi<S extends AnyObject = AnyObject> extends PreInitPluginApi {
|
||||
unscoped: UnscopedInitPluginApi
|
||||
logger: InstanceType<DiscordModules.Logger>
|
||||
/**
|
||||
* 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<S>
|
||||
}
|
||||
|
||||
/**
|
||||
* The plugin API.
|
||||
* Available in the `start` and `stop` phase.
|
||||
*/
|
||||
export interface PluginApi extends InitPluginApi {
|
||||
// settings: typeof import('@revenge-mod/settings')
|
||||
export interface PluginApi<S extends AnyObject = AnyObject> extends InitPluginApi<S> {
|
||||
unscoped: UnscopedPluginApi
|
||||
}
|
||||
|
||||
|
|
@ -108,7 +119,11 @@ export interface PluginManifest {
|
|||
icon?: string
|
||||
}
|
||||
|
||||
export interface PluginLifecycles {
|
||||
export interface PluginOptions<S extends AnyObject = AnyObject> extends PluginLifecycles<S> {
|
||||
storage?: Omit<StorageOptions<S>, 'directory'>
|
||||
}
|
||||
|
||||
export interface PluginLifecycles<S extends AnyObject = AnyObject> {
|
||||
/**
|
||||
* Runs as soon as possible with very limited APIs.
|
||||
* Before the index module (module 0)'s factory is run.
|
||||
|
|
@ -122,22 +137,22 @@ export interface PluginLifecycles {
|
|||
*
|
||||
* @param api Plugin API (limited).
|
||||
*/
|
||||
init?: (api: InitPluginApi) => any
|
||||
init?: (api: InitPluginApi<S>) => any
|
||||
/**
|
||||
* Runs when the plugin can be started with all APIs available.
|
||||
*
|
||||
* @param api Plugin API.
|
||||
*/
|
||||
start?: (api: PluginApi) => any
|
||||
start?: (api: PluginApi<S>) => any
|
||||
/**
|
||||
* Runs when the plugin is stopped.
|
||||
*
|
||||
* @param api Plugin API.
|
||||
*/
|
||||
stop?: (api: PluginApi) => any
|
||||
stop?: (api: PluginApi<S>) => any
|
||||
}
|
||||
|
||||
export interface Plugin {
|
||||
export interface Plugin<S extends AnyObject = AnyObject> {
|
||||
// TODO(plugins): support plugin bundles
|
||||
// /**
|
||||
// * The plugin bundle this plugin belongs to.
|
||||
|
|
@ -150,7 +165,7 @@ export interface Plugin {
|
|||
/**
|
||||
* The plugin lifecycles.
|
||||
*/
|
||||
lifecycles: PluginLifecycles
|
||||
lifecycles: PluginLifecycles<S>
|
||||
|
||||
/**
|
||||
* The plugin flags.
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue