/** * Manifest Plugin Loader — resolves, validates, and loads plugins. * * Plugins are declared in `manifest.config.yaml` under `plugins` and loaded * via dynamic import. The loader validates plugin shape, checks compatibility * versions, registers projections, and builds composite registries for stores, * audit sinks, builtins, and CLI commands. * * Usage: * ```ts * import { loadPlugins } from '@angriff36/manifest/plugin-loader'; * const result = await loadPlugins(declarations, { manifestVersion: '1.0.5' }); * ``` */ import { type ManifestPlugin, type Store, type CliCommandPlugin, type CliProgramLike } from './plugin-api.js'; import type { AuditSink, AuditRecord } from './audit/audit-sink.js'; /** * A plugin declaration from manifest.config.yaml. */ export interface PluginDeclaration { /** npm package name or relative file path. */ module: string; /** Plugin-specific options (forwarded to onLoad and factories). */ options?: Record; /** Whether the plugin is active (default: true). */ enabled?: boolean; /** * Config G9 — explicit load priority. Lower numbers load first. * When omitted, the plugin sorts after any ordered entry (ties by `module`). */ order?: number; /** * Config G9 — capability tags this declaration advertises * (`storeAdapter`, `auditSink`, `builtin`, `cliCommand`, `projection`, or host tags). */ capabilities?: string[]; } export type PluginDiagnosticSeverity = 'error' | 'warning' | 'info'; export interface PluginDiagnostic { severity: PluginDiagnosticSeverity; pluginName?: string; message: string; } export interface PluginLoaderOptions { /** Current Manifest package version for compatibility checks. */ manifestVersion: string; /** Base directory for resolving relative plugin paths. */ cwd?: string; } /** * Composite store provider built from all loaded store adapter plugins. * Maps entity names to stores via scheme lookup. */ export type CompositeStoreProvider = (entityName: string, scheme?: string) => Store | undefined; /** * Composite audit sink that fans out to all loaded sink plugins. */ export interface CompositeAuditSink { emit(record: AuditRecord): Promise; readonly sinkIds: string[]; } /** * Result of loading all plugins. */ export interface LoadedPluginRegistries { /** Store provider aggregating all loaded store adapters. */ storeProvider: CompositeStoreProvider; /** Builtins map from all loaded plugins. */ builtins: Map unknown>; /** Audit sink factories by id. */ auditSinkFactories: Map) => AuditSink | Promise>; /** CLI command registrations from all loaded plugins. */ cliCommands: Array<{ pluginName: string; command: CliCommandPlugin; }>; /** All loaded plugins (for inspection). */ loadedPlugins: ManifestPlugin[]; /** Diagnostics from the loading process. */ diagnostics: PluginDiagnostic[]; /** * Config G9 — module paths in the order plugins were actually applied * (after `order` sort; disabled entries omitted). */ loadOrder: string[]; /** * Config G9 — capability tags from each successfully loaded plugin's * declaration (key = plugin manifest name). */ declaredCapabilities: Map; } /** * Minimal SemVer range check. Supports: * - Exact: "1.0.5" * - GTE: ">=1.0.0" * - Caret: "^1.0.0" (>=1.0.0 <2.0.0), "^0.3.0" (>=0.3.0 <0.4.0) * - Tilde: "~1.0.0" (>=1.0.0 <1.1.0) * - Compound: ">=1.0.0 <2.0.0" */ declare function satisfiesSemVerRange(version: string, range: string): boolean; declare function validatePluginShape(raw: unknown, moduleName: string): { plugin: ManifestPlugin; errors: string[]; }; /** * Load and validate all declared plugins. * * @param declarations - Plugin declarations from manifest.config.yaml * @param opts - Loader options * @returns Composite registries and diagnostics */ export declare function loadPlugins(declarations: PluginDeclaration[], opts: PluginLoaderOptions): Promise; /** * Register all plugin CLI commands with a CLI program. */ export declare function registerPluginCliCommands(cliCommands: Array<{ pluginName: string; command: CliCommandPlugin; }>, program: CliProgramLike): void; /** * The subset of the runtime engine's `RuntimeOptions` that plugins populate. * Kept structural (not an import of `RuntimeOptions`) so the loader stays * decoupled from the runtime engine module — the two `Store` shapes are * identical, so the fragment assigns cleanly into `new RuntimeEngine(...)`. */ export interface PluginRuntimeOptions { /** Entity-keyed store provider (engine contract), adapted from the scheme-keyed composite. */ storeProvider?: (entityName: string) => Store | undefined; /** Custom builtins map, passed straight through to the engine. */ customBuiltins?: Map unknown>; /** A single instantiated audit sink, when one is selected. */ auditSink?: AuditSink; } /** * Inputs that let the composition resolve the two seams the loader cannot infer * on its own: which store scheme each entity uses, and which audit sink to build. */ export interface PluginRuntimeCompositionOptions { /** * IR store declarations (`ir.stores`). The engine's `storeProvider` is keyed * by entity name only, but the composite provider is keyed by scheme, so the * composition needs this map to bridge entity → scheme. */ stores?: ReadonlyArray<{ entity: string; target: string; }>; /** * Which audit sink id to instantiate. Required only when more than one sink * is registered; with exactly one, it is selected automatically. */ auditSinkId?: string; /** Options forwarded to the selected audit sink factory. */ auditSinkOptions?: Record; } /** * Map loaded plugin registries into the fragment of `RuntimeOptions` that an * app passes to `new RuntimeEngine(ir, context, options)`. This is the * documented seam between config-declared plugins and the runtime: apps call * `loadPlugins` once, then spread this fragment into their runtime options. * * Async because audit sink factories may be async. */ export declare function pluginRegistriesToRuntimeOptions(registries: LoadedPluginRegistries, options?: PluginRuntimeCompositionOptions): Promise; export { satisfiesSemVerRange, validatePluginShape }; //# sourceMappingURL=plugin-loader.d.ts.map