From 4cc7f88a376f2cda4f5226f0e9bec9f7e7ca4772 Mon Sep 17 00:00:00 2001 From: AbdulSnk Date: Sun, 29 Mar 2026 14:59:19 +0100 Subject: [PATCH] feat: implement self-contained plugin system architecture in middleware --- backend/src/core/PluginLoader.ts | 8 +++++++ backend/src/core/PluginManager.ts | 37 ++++++++++++++++++++++++++++++ backend/src/core/lifecycle.ts | 5 ++++ backend/src/core/pluginRegistry.ts | 20 ++++++++++++++++ backend/src/core/types.ts | 16 +++++++++++++ 5 files changed, 86 insertions(+) create mode 100644 backend/src/core/PluginLoader.ts create mode 100644 backend/src/core/PluginManager.ts create mode 100644 backend/src/core/lifecycle.ts create mode 100644 backend/src/core/pluginRegistry.ts create mode 100644 backend/src/core/types.ts diff --git a/backend/src/core/PluginLoader.ts b/backend/src/core/PluginLoader.ts new file mode 100644 index 0000000..f5c8edc --- /dev/null +++ b/backend/src/core/PluginLoader.ts @@ -0,0 +1,8 @@ +import { Plugin } from "./types"; + +export class PluginLoader { + static async load(path: string): Promise { + const mod = await import(path); + return mod.default; + } +} \ No newline at end of file diff --git a/backend/src/core/PluginManager.ts b/backend/src/core/PluginManager.ts new file mode 100644 index 0000000..d52043d --- /dev/null +++ b/backend/src/core/PluginManager.ts @@ -0,0 +1,37 @@ +import { Plugin, PluginContext } from "./types"; +import { PluginRegistry } from "./PluginRegistry"; + +export class PluginManager { + private registry = new PluginRegistry(); + private context: PluginContext; + + constructor(context: PluginContext = {}) { + this.context = context; + } + + register(plugin: Plugin) { + this.registry.register(plugin); + } + + async initAll() { + for (const plugin of this.registry.getAll()) { + await plugin.init?.(this.context); + } + } + + async startAll() { + for (const plugin of this.registry.getAll()) { + await plugin.start?.(); + } + } + + async stopAll() { + for (const plugin of this.registry.getAll()) { + await plugin.stop?.(); + } + } + + getPlugin(name: string) { + return this.registry.get(name); + } +} \ No newline at end of file diff --git a/backend/src/core/lifecycle.ts b/backend/src/core/lifecycle.ts new file mode 100644 index 0000000..49d9b5a --- /dev/null +++ b/backend/src/core/lifecycle.ts @@ -0,0 +1,5 @@ +export enum PluginLifecycle { + INIT = "init", + START = "start", + STOP = "stop", + } \ No newline at end of file diff --git a/backend/src/core/pluginRegistry.ts b/backend/src/core/pluginRegistry.ts new file mode 100644 index 0000000..2e417a3 --- /dev/null +++ b/backend/src/core/pluginRegistry.ts @@ -0,0 +1,20 @@ +import { Plugin } from "./types"; + +export class PluginRegistry { + private plugins: Map = new Map(); + + register(plugin: Plugin) { + if (this.plugins.has(plugin.name)) { + throw new Error(`Plugin ${plugin.name} already registered`); + } + this.plugins.set(plugin.name, plugin); + } + + get(name: string): Plugin | undefined { + return this.plugins.get(name); + } + + getAll(): Plugin[] { + return Array.from(this.plugins.values()); + } +} \ No newline at end of file diff --git a/backend/src/core/types.ts b/backend/src/core/types.ts new file mode 100644 index 0000000..a414cc2 --- /dev/null +++ b/backend/src/core/types.ts @@ -0,0 +1,16 @@ +export type PluginContext = { + config?: Record; + logger?: { + log: (...args: any[]) => void; + error: (...args: any[]) => void; + }; +}; + +export interface Plugin { + name: string; + version: string; + + init?(context: PluginContext): Promise | void; + start?(): Promise | void; + stop?(): Promise | void; +} \ No newline at end of file