import { Document, Documents } from '../../documents/documents.js'; import { DatabaseMigration, Databases } from '../../databases/databases.js'; import { EventEmitter } from '../../utils/eventemitter.js'; import { Plugins } from '../plugins.js'; import { z, ZodSchema } from 'zod'; import { PluginActionApi } from './plugin.api.js'; type PluginOptions = { plugins: Plugins; documents: Documents; databases: Databases; configs: { local?: TLocalConfig; shared?: TSharedConfig; }; }; type PluginEvents = { configChange: (config: unknown) => void; }; abstract class Plugin< TLocalConfig extends ZodSchema = ZodSchema, TSharedConfig extends ZodSchema = ZodSchema, TActions extends PluginActionApi = PluginActionApi, > extends EventEmitter { #options: PluginOptions; constructor(options: PluginOptions) { super(); this.#options = options; } public get documents(): Documents { return this.#options.documents; } public readonly configSchemas?: { local?: TLocalConfig; shared?: TSharedConfig; }; public getDB = async (name: string, migrations: DatabaseMigration[]) => { const { databases } = this.#options; const scopedName = `plugins:${this.name}:${name}`; return databases.get({ name: scopedName, migrations }); }; public get configs(): { local?: z.infer; shared?: z.infer; } { return this.#options.configs; } public setConfigs = async (configs: { local?: z.infer; shared?: z.infer }) => { this.#options.configs = configs; await this.emit('configChange', configs); }; public abstract readonly name: string; public actions?: TActions; public onLoad?: () => Promise; public onUnload?: () => Promise; public onLoaded?: () => Promise; public process?: (document: Document) => Promise; /*public getPlugin = async (plugin: new (...args: any) => T): Promise< T['api'] extends (...args: any[]) => infer R ? R : never > => { const { plugins } = this.#options; const instance = await plugins.get(plugin); return instance.api?.() as any; }*/ public action = async , TAction extends keyof TPlugin['actions']>( plugin: new (...args: any[]) => TPlugin, action: TAction, input: Exclude[TAction]['input'], undefined> extends ZodSchema ? z.infer[TAction]['input'], undefined>> : undefined, ): Promise< Exclude[TAction]['output'], undefined> extends ZodSchema ? z.infer[TAction]['output'], undefined>> : undefined > => { const { plugins } = this.#options; const instance = await plugins.get(plugin); const { actions } = instance; if (!actions) { throw new Error(`Plugin ${plugin.name} does not have actions`); } const actionDef = actions[action]; if (!actionDef) { throw new Error(`Plugin ${plugin.name} does not have action ${String(action)}`); } actionDef.input?.parse(input); return (await actionDef.handle?.(input)) as any; }; } type PluginConstructor< TLocalConfig extends ZodSchema = ZodSchema, TSharedConfig extends ZodSchema = ZodSchema, T extends Plugin = Plugin, > = new (options: PluginOptions) => T; export { Plugin, type PluginOptions, type PluginConstructor };