mirror of
https://github.com/karakeep-app/karakeep.git
synced 2026-10-02 13:56:53 +02:00
111 lines
3.0 KiB
TypeScript
111 lines
3.0 KiB
TypeScript
// Implementation inspired from Outline
|
|
|
|
import type { AssetStore } from "./assetdb";
|
|
import type { QueueClient } from "./queueing";
|
|
import type { RateLimitClient } from "./ratelimiting";
|
|
import type { VectorStoreClient } from "./vectorStore";
|
|
import logger from "./logger";
|
|
import { SearchIndexClient } from "./search";
|
|
|
|
export enum PluginType {
|
|
AssetStore = "assetstore",
|
|
Search = "search",
|
|
Queue = "queue",
|
|
RateLimit = "ratelimit",
|
|
VectorStore = "vectorstore",
|
|
}
|
|
|
|
interface PluginTypeMap {
|
|
[PluginType.AssetStore]: AssetStore;
|
|
[PluginType.Search]: SearchIndexClient;
|
|
[PluginType.Queue]: QueueClient;
|
|
[PluginType.RateLimit]: RateLimitClient;
|
|
[PluginType.VectorStore]: VectorStoreClient;
|
|
}
|
|
|
|
export interface TPlugin<T extends PluginType> {
|
|
type: T;
|
|
name: string;
|
|
provider: PluginProvider<PluginTypeMap[T]>;
|
|
}
|
|
|
|
export interface PluginProvider<T> {
|
|
getClient(): Promise<T | null>;
|
|
}
|
|
|
|
// Preserve the key-dependent value type: for K, store TPlugin<K>[]
|
|
type ProviderMap = { [K in PluginType]: TPlugin<K>[] };
|
|
|
|
const pluginProvidersKey = "__karakeep_plugins_providers__";
|
|
|
|
function createProviderMap(): ProviderMap {
|
|
return {
|
|
[PluginType.AssetStore]: [],
|
|
[PluginType.Search]: [],
|
|
[PluginType.Queue]: [],
|
|
[PluginType.RateLimit]: [],
|
|
[PluginType.VectorStore]: [],
|
|
};
|
|
}
|
|
|
|
const globalPluginState = globalThis as typeof globalThis & {
|
|
[pluginProvidersKey]?: ProviderMap;
|
|
};
|
|
|
|
export class PluginManager {
|
|
private static providers: ProviderMap = (globalPluginState[
|
|
pluginProvidersKey
|
|
] ??= createProviderMap());
|
|
|
|
private static providersFor<T extends PluginType>(type: T): TPlugin<T>[] {
|
|
PluginManager.providers[type] ??= [] as ProviderMap[T];
|
|
return PluginManager.providers[type] as TPlugin<T>[];
|
|
}
|
|
|
|
static register<T extends PluginType>(plugin: TPlugin<T>): void {
|
|
const providers = PluginManager.providersFor(plugin.type);
|
|
const existingProvider = providers.findIndex((p) => p.name === plugin.name);
|
|
if (existingProvider >= 0) {
|
|
return;
|
|
}
|
|
providers.push(plugin);
|
|
}
|
|
|
|
static async getClient<T extends PluginType>(
|
|
type: T,
|
|
): Promise<PluginTypeMap[T] | null> {
|
|
const providers = PluginManager.providersFor(type);
|
|
if (providers.length === 0) {
|
|
return null;
|
|
}
|
|
return await providers[providers.length - 1]!.provider.getClient();
|
|
}
|
|
|
|
static isRegistered<T extends PluginType>(type: T): boolean {
|
|
return PluginManager.providersFor(type).length > 0;
|
|
}
|
|
|
|
static getPluginName<T extends PluginType>(type: T): string | null {
|
|
const providers = PluginManager.providersFor(type);
|
|
if (providers.length === 0) {
|
|
return null;
|
|
}
|
|
return providers[providers.length - 1]!.name;
|
|
}
|
|
|
|
static logAllPlugins() {
|
|
logger.info("Plugins (Last one wins):");
|
|
for (const type of Object.values(PluginType)) {
|
|
logger.info(` ${type}:`);
|
|
const plugins = PluginManager.providers[type];
|
|
if (!plugins) {
|
|
logger.info(" - None");
|
|
continue;
|
|
}
|
|
for (const plugin of plugins) {
|
|
logger.info(` - ${plugin.name}`);
|
|
}
|
|
}
|
|
}
|
|
}
|