New Research: Supply Chain Attack on Axios Pulls Malicious Dependency from npm.Details →
Socket
Book a DemoSign in
Socket

@libria/plugin-loader

Package Overview
Dependencies
Maintainers
1
Versions
5
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@libria/plugin-loader - npm Package Compare versions

Comparing version
0.1.1
to
2.0.0-alpha
+1
-1
dist/index.cjs

@@ -1,2 +0,2 @@

var e=Object.create,t=Object.defineProperty,n=Object.getOwnPropertyDescriptor,r=Object.getOwnPropertyNames,i=Object.getPrototypeOf,a=Object.prototype.hasOwnProperty,o=(e,i,o,s)=>{if(i&&typeof i==`object`||typeof i==`function`)for(var c=r(i),l=0,u=c.length,d;l<u;l++)d=c[l],!a.call(e,d)&&d!==o&&t(e,d,{get:(e=>i[e]).bind(null,d),enumerable:!(s=n(i,d))||s.enumerable});return e},s=(n,r,a)=>(a=n==null?{}:e(i(n)),o(r||!n||!n.__esModule?t(a,`default`,{value:n,enumerable:!0}):a,n));let c=require(`fs-extra`);c=s(c);let l=require(`fast-glob`);l=s(l);let u=require(`path`);u=s(u);let d=require(`url`),f=require(`node:module`);function p(e,t,n){return{pluginType:e,name:t,api:n}}async function m(e,t){let n=[];if(e.includes(`*`)||e.includes(`{`)||e.includes(`?`)){let r=await(0,l.default)(e.replace(/\\/g,`/`),{onlyDirectories:!0,absolute:!0});for(let e of r){let r=u.default.join(e,`plugin.json`);if(!await c.default.pathExists(r))continue;let i=await c.default.readJson(r);t&&i.pluginType!==t||n.push({...i,__dir:e})}}else{if(!await c.default.pathExists(e))return[];let r=await c.default.readdir(e);for(let i of r){let r=u.default.join(e,i);if(!(await c.default.stat(r)).isDirectory())continue;let a=u.default.join(r,`plugin.json`);if(!await c.default.pathExists(a))continue;let o=await c.default.readJson(a);t&&o.pluginType!==t||n.push({...o,__dir:r})}}return n}var h=class extends Error{constructor(e,t){super(`Failed to load plugin "${e}".\n${String(t)}`),this.name=`PluginLoadError`,this.pluginName=e,this.cause=t}},g=class extends Error{constructor(e){super(`Plugin "${e}" has invalid export`),this.name=`PluginInvalidExportError`,this.pluginName=e}},_=class extends Error{constructor(e,t,n){super(`Plugin type mismatch for "${e}": "${n}" !== "${t}"`),this.name=`PluginTypeMismatchError`,this.pluginName=e,this.expected=t,this.actual=n}};const v=(0,f.createRequire)(require(`url`).pathToFileURL(__filename).href);async function y(e){let t,n;if(e.module)try{t=await import((0,d.pathToFileURL)(u.default.resolve(e.__dir,e.module)).href)}catch(e){n=e}if(!t&&e.main)try{t=v(u.default.resolve(e.__dir,e.main))}catch(e){n=e}if(!t)throw new h(e.name,n);let r=t.default??t;if(!r||typeof r!=`object`)throw new g(e.name);if(r.pluginType!==e.pluginType)throw new _(e.name,e.pluginType,r.pluginType);return r}async function b(e,t){let n=await m(e,t),r=[];for(let e of n){let t=await y(e);r.push(t)}return r}exports.PluginInvalidExportError=g,exports.PluginLoadError=h,exports.PluginTypeMismatchError=_,exports.definePlugin=p,exports.findPlugins=m,exports.loadAllPlugins=b,exports.loadPlugin=y;
var e=Object.create,t=Object.defineProperty,n=Object.getOwnPropertyDescriptor,r=Object.getOwnPropertyNames,i=Object.getPrototypeOf,a=Object.prototype.hasOwnProperty,o=(e,i,o,s)=>{if(i&&typeof i==`object`||typeof i==`function`)for(var c=r(i),l=0,u=c.length,d;l<u;l++)d=c[l],!a.call(e,d)&&d!==o&&t(e,d,{get:(e=>i[e]).bind(null,d),enumerable:!(s=n(i,d))||s.enumerable});return e},s=(n,r,a)=>(a=n==null?{}:e(i(n)),o(r||!n||!n.__esModule?t(a,`default`,{value:n,enumerable:!0}):a,n));let c=require(`path`);c=s(c);let l=require(`fast-glob`);l=s(l);let u=require(`fs-extra`);u=s(u);let d=require(`semver`);d=s(d);let f=require(`node:module`),p=require(`url`),m=require(`chokidar`);var h=class extends Error{constructor(e,t){super(`Failed to load plugin "${e}".\n${String(t)}`),this.name=`PluginLoadError`,this.pluginName=e,this.cause=t}},g=class extends Error{constructor(e){super(`Plugin "${e}" has invalid export`),this.name=`PluginInvalidExportError`,this.pluginName=e}},_=class extends Error{constructor(e,t,n){super(`Plugin type mismatch for "${e}": "${n}" !== "${t}"`),this.name=`PluginTypeMismatchError`,this.pluginName=e,this.expected=t,this.actual=n}},v=class extends Error{constructor(e){super(`Duplicate plugin "${e}"`),this.id=e}},y=class extends Error{constructor(e){super(`Plugin "${e}" not found`),this.name=`PluginNotFoundError`,this.id=e}},b=class extends Error{constructor(e,t){super(`Manifest not found for plugin "${e}" in "${t}"`),this.name=`ManifestNotFoundError`,this.pluginId=e,this.dir=t}},x=class extends Error{constructor(e){super(`Circular dependency detected: ${e.join(` -> `)}`),this.name=`CircularDependencyError`,this.cycle=e}},S=class extends Error{constructor(e,t){let n=t?`Dependency "${e}" not found (required by "${t}")`:`Dependency "${e}" not found`;super(n),this.name=`DependencyNotFoundError`,this.dependencyId=e,this.requestedBy=t}},C=class extends Error{constructor(e,t,n,r){let i=r?`Version mismatch: ${e}@${t} does not satisfy ${n} (required by "${r}")`:`Version mismatch: ${e}@${t} does not satisfy ${n}`;super(i),this.name=`VersionMismatchError`,this.packageId=e,this.actualVersion=t,this.requiredVersion=n,this.requestedBy=r}},w=class{constructor(){this.plugins=new Map}register(e,t,n){if(this.plugins.has(e))throw new v(e);this.plugins.set(e,{plugin:t,metadata:n})}unregister(e){let t=this.plugins.get(e);if(t)return this.plugins.delete(e),t.plugin}getPlugin(e){let t=this.plugins.get(e);if(!t)throw new y(e);return t.plugin.api}hasPlugin(e){return this.plugins.has(e)}getPluginInstance(e){return this.plugins.get(e)?.plugin}getPluginMetadata(e){return this.plugins.get(e)?.metadata}getPluginIds(){return[...this.plugins.keys()]}getPluginsByType(e){let t=[];for(let{metadata:n}of this.plugins.values())n.pluginType===e&&t.push(n);return t}getAllMetadata(){return[...this.plugins.values()].map(e=>e.metadata)}};function T(e){return e}async function E(e,t){let n=[];if(e.includes(`*`)||e.includes(`{`)||e.includes(`?`)){let r=await(0,l.default)(e.replace(/\\/g,`/`),{onlyDirectories:!0,absolute:!0});for(let e of r){let r=c.default.join(e,`plugin.json`);if(!await u.default.pathExists(r))continue;let i=await u.default.readJson(r);t&&i.pluginType!==t||n.push({...i,__dir:e})}}else{if(!await u.default.pathExists(e))return[];let r=c.default.join(e,`plugin.json`);if(await u.default.pathExists(r)){let i=await u.default.readJson(r);return(!t||i.pluginType===t)&&n.push({...i,__dir:c.default.resolve(e)}),n}let i=await u.default.readdir(e);for(let r of i){let i=c.default.join(e,r);if(!(await u.default.stat(i)).isDirectory())continue;let a=c.default.join(i,`plugin.json`);if(!await u.default.pathExists(a))continue;let o=await u.default.readJson(a);t&&o.pluginType!==t||n.push({...o,__dir:i})}}return n}function D(e){let t=new Map(e.map(e=>[e.id,e])),n=[],r=new Set,i=new Set;function a(e,o,s=[]){if(i.has(e)){if(o){let n=t.get(e);if(n&&!d.default.satisfies(n.version,o))throw new C(e,n.version,o,s[s.length-1])}return}if(r.has(e))throw new x([...s,e].slice(s.indexOf(e)));let c=t.get(e);if(!c)throw new S(e,s[s.length-1]);if(o&&!d.default.satisfies(c.version,o))throw new C(e,c.version,o,s[s.length-1]);if(r.add(e),c.dependencies)for(let t of c.dependencies)a(t.id,t.version,[...s,e]);r.delete(e),i.add(e),n.push(c)}for(let t of e)a(t.id);return n}const O=(0,f.createRequire)(require(`url`).pathToFileURL(__filename).href);function k(e){return typeof e==`object`&&!!e&&`id`in e&&`pluginType`in e&&`create`in e&&typeof e.create==`function`}async function A(e){let t,n;if(e.module)try{t=await import(`${(0,p.pathToFileURL)(c.default.resolve(e.__dir,e.module)).href}?t=${Date.now()}`)}catch(e){n=e}if(!t&&e.main)try{let n=c.default.resolve(e.__dir,e.main);delete O.cache[O.resolve(n)],t=O(n)}catch(e){n=e}if(!t)throw new h(e.id,n);let r=t.default??t;if(!k(r))throw new g(e.id);return r}function j(e){if(e.main){let t=c.default.resolve(e.__dir,e.main);try{delete O.cache[O.resolve(t)]}catch{}}}function M(e){return{id:e.id,name:e.name,pluginType:e.pluginType,version:e.version,description:e.description,dependencies:e.dependencies,dir:e.__dir}}var N=class{constructor(){this.manifests=new Map,this.watcher=null,this.watchPatterns=[],this.context=new w}async loadPlugins(e){let t=[];for(let n of e){let e=await E(n);t.push(...e)}let n=D(t);for(let e of n)await this.loadSinglePlugin(e)}async loadSinglePlugin(e){let t=await A(e),n=await Promise.resolve(t.create(this.context)),r=M(e);this.context.register(e.id,n,r),this.manifests.set(e.id,e),n.onLoad&&await Promise.resolve(n.onLoad())}async unloadSinglePlugin(e){let t=this.context.getPluginInstance(e);t?.onUnload&&await Promise.resolve(t.onUnload()),this.context.unregister(e),this.manifests.delete(e)}async reloadPlugin(e){let t=this.manifests.get(e);if(!t)throw new y(e);j(t),await this.unloadSinglePlugin(e);let[n]=await E(t.__dir);if(!n)throw new b(e,t.__dir);await this.loadSinglePlugin(n)}async unloadPlugin(e){if(!this.hasPlugin(e))throw new y(e);await this.unloadSinglePlugin(e)}async watch(e,t){this.watcher&&await this.stopWatching(),this.watchPatterns=e;let n=[];for(let[,e]of this.manifests)n.push(e.__dir);n.length!==0&&(this.watcher=(0,m.watch)(n,{ignoreInitial:!0,awaitWriteFinish:{stabilityThreshold:100,pollInterval:50}}),this.watcher.on(`change`,async e=>{for(let[n,r]of this.manifests)if(e.startsWith(r.__dir)){try{await this.reloadPlugin(n),t?.(n,`reload`)}catch(e){t?.(n,`error`,e instanceof Error?e:Error(String(e)))}break}}))}async stopWatching(){this.watcher&&=(await this.watcher.close(),null)}getPlugin(e){return this.context.getPlugin(e)}hasPlugin(e){return this.context.hasPlugin(e)}getPluginIds(){return this.context.getPluginIds()}getPluginMetadata(e){return this.context.getPluginMetadata(e)}getPluginsByType(e){return this.context.getPluginsByType(e)}getAllMetadata(){return this.context.getAllMetadata()}getContext(){return this.context}async shutdown(){await this.stopWatching();let e=[...this.manifests.keys()].reverse();for(let t of e)await this.unloadSinglePlugin(t)}};exports.CircularDependencyError=x,exports.DefaultPluginContext=w,exports.DependencyNotFoundError=S,exports.DuplicatePluginError=v,exports.ManifestNotFoundError=b,exports.PluginInvalidExportError=g,exports.PluginLoadError=h,exports.PluginManager=N,exports.PluginNotFoundError=y,exports.PluginTypeMismatchError=_,exports.VersionMismatchError=C,exports.clearPluginCache=j,exports.definePlugin=T,exports.findPlugins=E,exports.loadPlugin=A,exports.topologicalSort=D;
//# sourceMappingURL=index.cjs.map

@@ -1,1 +0,1 @@

{"version":3,"file":"index.cjs","names":["fs","require"],"sources":["../src/define-plugin.ts","../src/find-plugins.ts","../src/types.ts","../src/load-plugin.ts","../src/load-all-plugins.ts"],"sourcesContent":["import {LibriaPlugin} from \"./types\";\r\n\r\nexport function definePlugin<T>(\r\n pluginType: string,\r\n name: string,\r\n api: T\r\n): LibriaPlugin<T> {\r\n return {\r\n pluginType: pluginType,\r\n name,\r\n api\r\n };\r\n}","import {PluginManifest} from \"./types\";\r\nimport fs from \"fs-extra\";\r\nimport fg from \"fast-glob\";\r\nimport path from \"path\";\r\n\r\nexport async function findPlugins(\r\n pattern: string,\r\n pluginType?: string\r\n): Promise<PluginManifest[]> {\r\n const manifests: PluginManifest[] = [];\r\n\r\n // Check if pattern is a glob pattern or a simple path\r\n const isGlob = pattern.includes('*') || pattern.includes('{') || pattern.includes('?');\r\n\r\n if (isGlob) {\r\n // Normalize path separators for fast-glob (it expects forward slashes)\r\n const normalizedPattern = pattern.replace(/\\\\/g, '/');\r\n\r\n // Use fast-glob to find all matching directories\r\n const pluginDirs = await fg(normalizedPattern, {\r\n onlyDirectories: true,\r\n absolute: true,\r\n });\r\n\r\n for (const dir of pluginDirs) {\r\n const manifestPath = path.join(dir, 'plugin.json');\r\n if (!(await fs.pathExists(manifestPath))) continue;\r\n\r\n const raw = await fs.readJson(manifestPath);\r\n\r\n if (pluginType && raw.pluginType !== pluginType) continue;\r\n\r\n manifests.push({\r\n ...raw,\r\n __dir: dir\r\n });\r\n }\r\n } else {\r\n // Original behavior for simple directory paths\r\n if (!(await fs.pathExists(pattern))) return [];\r\n\r\n const entries = await fs.readdir(pattern);\r\n\r\n for (const entry of entries) {\r\n const fullPath = path.join(pattern, entry);\r\n const stat = await fs.stat(fullPath);\r\n\r\n if (!stat.isDirectory()) continue;\r\n\r\n const manifestPath = path.join(fullPath, 'plugin.json');\r\n if (!(await fs.pathExists(manifestPath))) continue;\r\n\r\n const raw = await fs.readJson(manifestPath);\r\n\r\n if (pluginType && raw.pluginType !== pluginType) continue;\r\n\r\n manifests.push({\r\n ...raw,\r\n __dir: fullPath\r\n });\r\n }\r\n }\r\n\r\n return manifests;\r\n}","export interface LibriaPlugin<T = unknown> {\r\n readonly pluginType: string;\r\n readonly name: string;\r\n readonly api: T;\r\n}\r\n\r\nexport interface PluginManifest {\r\n readonly name: string;\r\n readonly pluginType: string;\r\n\r\n readonly main?: string; // cjs\r\n readonly module?: string; // esm\r\n readonly types?: string;\r\n\r\n // resolved absolute path to the plugin folder\r\n readonly __dir: string;\r\n}\r\n\r\n// Plugin Errors\r\n\r\nexport class PluginLoadError extends Error {\r\n readonly pluginName: string;\r\n readonly cause: unknown;\r\n\r\n constructor(pluginName: string, cause: unknown) {\r\n super(`Failed to load plugin \"${pluginName}\".\\n${String(cause)}`);\r\n this.name = 'PluginLoadError';\r\n this.pluginName = pluginName;\r\n this.cause = cause;\r\n }\r\n}\r\n\r\nexport class PluginInvalidExportError extends Error {\r\n readonly pluginName: string;\r\n\r\n constructor(pluginName: string) {\r\n super(`Plugin \"${pluginName}\" has invalid export`);\r\n this.name = 'PluginInvalidExportError';\r\n this.pluginName = pluginName;\r\n }\r\n}\r\n\r\nexport class PluginTypeMismatchError extends Error {\r\n readonly pluginName: string;\r\n readonly expected: string;\r\n readonly actual: string;\r\n\r\n constructor(pluginName: string, expected: string, actual: string) {\r\n super(\r\n `Plugin type mismatch for \"${pluginName}\": ` +\r\n `\"${actual}\" !== \"${expected}\"`\r\n );\r\n this.name = 'PluginTypeMismatchError';\r\n this.pluginName = pluginName;\r\n this.expected = expected;\r\n this.actual = actual;\r\n }\r\n}","import {\r\n LibriaPlugin,\r\n PluginManifest,\r\n PluginLoadError,\r\n PluginInvalidExportError,\r\n PluginTypeMismatchError\r\n} from \"./types\";\r\nimport path from \"path\";\r\nimport {pathToFileURL} from \"url\";\r\nimport {createRequire} from \"node:module\";\r\n\r\nconst require = createRequire(import.meta.url);\r\n\r\nexport async function loadPlugin<T = unknown>(\r\n manifest: PluginManifest\r\n): Promise<LibriaPlugin<T>> {\r\n let mod: any;\r\n let lastError: unknown;\r\n\r\n // 1 Try ESM first\r\n if (manifest.module) {\r\n try {\r\n const esmPath = path.resolve(manifest.__dir, manifest.module);\r\n mod = await import(pathToFileURL(esmPath).href);\r\n } catch (err) {\r\n lastError = err;\r\n }\r\n }\r\n\r\n // 2 Fallback to CJS\r\n if (!mod && manifest.main) {\r\n try {\r\n const cjsPath = path.resolve(manifest.__dir, manifest.main);\r\n mod = require(cjsPath);\r\n } catch (err) {\r\n lastError = err;\r\n }\r\n }\r\n\r\n if (!mod) {\r\n throw new PluginLoadError(manifest.name, lastError);\r\n }\r\n\r\n // 3️⃣ Normalize export\r\n const plugin = (mod.default ?? mod) as LibriaPlugin<T>;\r\n\r\n if (!plugin || typeof plugin !== 'object') {\r\n throw new PluginInvalidExportError(manifest.name);\r\n }\r\n\r\n if (plugin.pluginType !== manifest.pluginType) {\r\n throw new PluginTypeMismatchError(\r\n manifest.name,\r\n manifest.pluginType,\r\n plugin.pluginType\r\n );\r\n }\r\n\r\n return plugin;\r\n}","import {LibriaPlugin} from \"./types\";\r\nimport {findPlugins} from \"./find-plugins\";\r\nimport {loadPlugin} from \"./load-plugin\";\r\n\r\nexport async function loadAllPlugins<T = unknown>(\r\n pattern: string,\r\n pluginType?: string\r\n): Promise<LibriaPlugin<T>[]> {\r\n const manifests = await findPlugins(pattern, pluginType);\r\n const plugins: LibriaPlugin<T>[] = [];\r\n\r\n for (const manifest of manifests) {\r\n const plugin = await loadPlugin<T>(manifest);\r\n plugins.push(plugin);\r\n }\r\n\r\n return plugins;\r\n}\r\n"],"mappings":"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"}
{"version":3,"file":"index.cjs","names":["fs","require"],"sources":["../src/types.ts","../src/default-plugin-context.ts","../src/define-plugin.ts","../src/find-plugins.ts","../src/helpers/topological-sort.ts","../src/load-plugin.ts","../src/plugin-manager.ts"],"sourcesContent":["// Lifecycle hooks that plugins can implement\nexport interface PluginLifecycle {\n /** Called after the plugin is registered and ready to use */\n onLoad?(): void | Promise<void>;\n /** Called before the plugin is unloaded (for hot-reload or shutdown) */\n onUnload?(): void | Promise<void>;\n}\n\nexport interface LibriaPlugin<T = unknown> extends PluginLifecycle {\n api: T;\n}\n\nexport interface PluginFactory<T = unknown> {\n readonly id: string;\n readonly pluginType: string;\n readonly name?: string;\n\n /** Create the plugin instance. Can be sync or async. */\n create<C extends PluginContext>(ctx: C): LibriaPlugin<T> | Promise<LibriaPlugin<T>>;\n}\n\nexport interface PluginContext {\n getPlugin<T = unknown>(id: string): T;\n hasPlugin(id: string): boolean;\n}\n\n/** Metadata stored for each loaded plugin */\nexport interface PluginMetadata {\n readonly id: string;\n readonly name?: string;\n readonly pluginType: string;\n readonly version: string;\n readonly description?: string;\n readonly dependencies?: { id: string; version: string }[];\n /** Absolute path to the plugin directory */\n readonly dir: string;\n}\n\nexport interface PluginManifest {\n readonly id: string;\n readonly name?: string;\n readonly pluginType: string;\n readonly version: string; // Semver\n readonly description?: string;\n\n readonly main?: string; // cjs\n readonly module?: string; // esm\n readonly types?: string;\n\n readonly dependencies?: { id: string; version: string }[];\n // resolved absolute path to the plugin folder\n readonly __dir: string;\n}\n\n// Plugin Errors\n\nexport class PluginLoadError extends Error {\n public readonly pluginName: string;\n public readonly cause: unknown;\n\n constructor(pluginName: string, cause: unknown) {\n super(`Failed to load plugin \"${pluginName}\".\\n${String(cause)}`);\n this.name = 'PluginLoadError';\n this.pluginName = pluginName;\n this.cause = cause;\n }\n}\n\nexport class PluginInvalidExportError extends Error {\n public readonly pluginName: string;\n\n constructor(pluginName: string) {\n super(`Plugin \"${pluginName}\" has invalid export`);\n this.name = 'PluginInvalidExportError';\n this.pluginName = pluginName;\n }\n}\n\nexport class PluginTypeMismatchError extends Error {\n public readonly pluginName: string;\n public readonly expected: string;\n public readonly actual: string;\n\n constructor(pluginName: string, expected: string, actual: string) {\n super(`Plugin type mismatch for \"${pluginName}\": ` + `\"${actual}\" !== \"${expected}\"`);\n this.name = 'PluginTypeMismatchError';\n this.pluginName = pluginName;\n this.expected = expected;\n this.actual = actual;\n }\n}\n\nexport class DuplicatePluginError extends Error {\n public readonly id: string;\n\n constructor(id: string) {\n super(`Duplicate plugin \"${id}\"`);\n\n this.id = id;\n }\n}\n\nexport class PluginNotFoundError extends Error {\n public readonly id: string;\n\n constructor(id: string) {\n super(`Plugin \"${id}\" not found`);\n this.name = 'PluginNotFoundError';\n this.id = id;\n }\n}\n\nexport class ManifestNotFoundError extends Error {\n public readonly pluginId: string;\n public readonly dir: string;\n\n constructor(pluginId: string, dir: string) {\n super(`Manifest not found for plugin \"${pluginId}\" in \"${dir}\"`);\n this.name = 'ManifestNotFoundError';\n this.pluginId = pluginId;\n this.dir = dir;\n }\n}\n\n// Dependency Resolution Errors\n\nexport class CircularDependencyError extends Error {\n public readonly cycle: string[];\n\n constructor(cycle: string[]) {\n super(`Circular dependency detected: ${cycle.join(' -> ')}`);\n this.name = 'CircularDependencyError';\n this.cycle = cycle;\n }\n}\n\nexport class DependencyNotFoundError extends Error {\n public readonly dependencyId: string;\n public readonly requestedBy?: string;\n\n constructor(dependencyId: string, requestedBy?: string) {\n const message = requestedBy\n ? `Dependency \"${dependencyId}\" not found (required by \"${requestedBy}\")`\n : `Dependency \"${dependencyId}\" not found`;\n super(message);\n this.name = 'DependencyNotFoundError';\n this.dependencyId = dependencyId;\n this.requestedBy = requestedBy;\n }\n}\n\nexport class VersionMismatchError extends Error {\n public readonly packageId: string;\n public readonly actualVersion: string;\n public readonly requiredVersion: string;\n public readonly requestedBy?: string;\n\n constructor(\n packageId: string,\n actualVersion: string,\n requiredVersion: string,\n requestedBy?: string\n ) {\n const message = requestedBy\n ? `Version mismatch: ${packageId}@${actualVersion} does not satisfy ${requiredVersion} (required by \"${requestedBy}\")`\n : `Version mismatch: ${packageId}@${actualVersion} does not satisfy ${requiredVersion}`;\n super(message);\n this.name = 'VersionMismatchError';\n this.packageId = packageId;\n this.actualVersion = actualVersion;\n this.requiredVersion = requiredVersion;\n this.requestedBy = requestedBy;\n }\n}\n","import {\n DuplicatePluginError,\n LibriaPlugin,\n PluginContext,\n PluginMetadata,\n PluginNotFoundError,\n} from './types';\n\ninterface RegisteredPlugin {\n plugin: LibriaPlugin;\n metadata: PluginMetadata;\n}\n\nexport class DefaultPluginContext implements PluginContext {\n private plugins: Map<string, RegisteredPlugin> = new Map();\n\n public register(id: string, plugin: LibriaPlugin, metadata: PluginMetadata): void {\n if (this.plugins.has(id)) {\n throw new DuplicatePluginError(id);\n }\n\n this.plugins.set(id, { plugin, metadata });\n }\n\n public unregister(id: string): LibriaPlugin | undefined {\n const registered = this.plugins.get(id);\n if (registered) {\n this.plugins.delete(id);\n return registered.plugin;\n }\n return undefined;\n }\n\n public getPlugin<T = unknown>(id: string): T {\n const registered = this.plugins.get(id);\n if (!registered) {\n throw new PluginNotFoundError(id);\n }\n\n return registered.plugin.api as T;\n }\n\n public hasPlugin(id: string): boolean {\n return this.plugins.has(id);\n }\n\n /** Get the raw LibriaPlugin instance (includes lifecycle hooks) */\n public getPluginInstance(id: string): LibriaPlugin | undefined {\n return this.plugins.get(id)?.plugin;\n }\n\n /** Get metadata for a specific plugin */\n public getPluginMetadata(id: string): PluginMetadata | undefined {\n return this.plugins.get(id)?.metadata;\n }\n\n /** Get all plugin IDs */\n public getPluginIds(): string[] {\n return [...this.plugins.keys()];\n }\n\n /** Get all plugins of a specific type */\n public getPluginsByType(pluginType: string): PluginMetadata[] {\n const result: PluginMetadata[] = [];\n for (const { metadata } of this.plugins.values()) {\n if (metadata.pluginType === pluginType) {\n result.push(metadata);\n }\n }\n return result;\n }\n\n /** Get all loaded plugin metadata */\n public getAllMetadata(): PluginMetadata[] {\n return [...this.plugins.values()].map(r => r.metadata);\n }\n}\n","import { PluginFactory } from './types';\n\nexport function definePlugin<T>(factory: PluginFactory<T>): PluginFactory<T> {\n return factory;\n}\n","import path from 'path';\n\nimport fg from 'fast-glob';\nimport fs from 'fs-extra';\n\nimport { PluginManifest } from './types';\n\nexport async function findPlugins(pattern: string, pluginType?: string): Promise<PluginManifest[]> {\n const manifests: PluginManifest[] = [];\n\n // Check if pattern is a glob pattern or a simple path\n const isGlob = pattern.includes('*') || pattern.includes('{') || pattern.includes('?');\n\n if (isGlob) {\n // Normalize path separators for fast-glob (it expects forward slashes)\n const normalizedPattern = pattern.replace(/\\\\/g, '/');\n\n // Use fast-glob to find all matching directories\n const pluginDirs = await fg(normalizedPattern, {\n onlyDirectories: true,\n absolute: true,\n });\n\n for (const dir of pluginDirs) {\n const manifestPath = path.join(dir, 'plugin.json');\n if (!(await fs.pathExists(manifestPath))) continue;\n\n const raw = await fs.readJson(manifestPath);\n\n if (pluginType && raw.pluginType !== pluginType) continue;\n\n manifests.push({\n ...raw,\n __dir: dir,\n });\n }\n } else {\n // Simple directory path - check if it exists\n if (!(await fs.pathExists(pattern))) return [];\n\n // First, check if the directory itself is a plugin (has plugin.json)\n const directManifestPath = path.join(pattern, 'plugin.json');\n if (await fs.pathExists(directManifestPath)) {\n const raw = await fs.readJson(directManifestPath);\n if (!pluginType || raw.pluginType === pluginType) {\n manifests.push({\n ...raw,\n __dir: path.resolve(pattern),\n });\n }\n return manifests;\n }\n\n // Otherwise, scan subdirectories for plugins\n const entries = await fs.readdir(pattern);\n\n for (const entry of entries) {\n const fullPath = path.join(pattern, entry);\n const stat = await fs.stat(fullPath);\n\n if (!stat.isDirectory()) continue;\n\n const manifestPath = path.join(fullPath, 'plugin.json');\n if (!(await fs.pathExists(manifestPath))) continue;\n\n const raw = await fs.readJson(manifestPath);\n\n if (pluginType && raw.pluginType !== pluginType) continue;\n\n manifests.push({\n ...raw,\n __dir: fullPath,\n });\n }\n }\n\n return manifests;\n}\n","import semver from 'semver';\n\nimport {\n CircularDependencyError,\n DependencyNotFoundError,\n PluginManifest,\n VersionMismatchError,\n} from '../types';\n\nexport function topologicalSort(packages: PluginManifest[]): PluginManifest[] {\n const packageMap = new Map(packages.map(pkg => [pkg.id, pkg]));\n const sorted: PluginManifest[] = [];\n const visiting = new Set<string>();\n const visited = new Set<string>();\n\n function visit(id: string, requiredVersion?: string, path: string[] = []): void {\n if (visited.has(id)) {\n // Still need to validate version even if already visited\n if (requiredVersion) {\n const pkg = packageMap.get(id);\n if (pkg && !semver.satisfies(pkg.version, requiredVersion)) {\n throw new VersionMismatchError(\n id,\n pkg.version,\n requiredVersion,\n path[path.length - 1]\n );\n }\n }\n return;\n }\n\n if (visiting.has(id)) {\n // Circular dependency detected\n const cycle = [...path, id].slice(path.indexOf(id));\n throw new CircularDependencyError(cycle);\n }\n\n const pkg = packageMap.get(id);\n if (!pkg) {\n throw new DependencyNotFoundError(id, path[path.length - 1]);\n }\n\n // Validate version requirement\n if (requiredVersion && !semver.satisfies(pkg.version, requiredVersion)) {\n throw new VersionMismatchError(id, pkg.version, requiredVersion, path[path.length - 1]);\n }\n\n visiting.add(id);\n\n // Visit all dependencies first\n if (pkg.dependencies) {\n for (const dep of pkg.dependencies) {\n visit(dep.id, dep.version, [...path, id]);\n }\n }\n\n visiting.delete(id);\n visited.add(id);\n sorted.push(pkg);\n }\n\n // Visit all packages\n for (const pkg of packages) {\n visit(pkg.id);\n }\n\n return sorted;\n}\n","import { createRequire } from 'node:module';\nimport path from 'path';\nimport { pathToFileURL } from 'url';\n\nimport { PluginFactory, PluginInvalidExportError, PluginLoadError, PluginManifest } from './types';\n\nconst require = createRequire(import.meta.url);\n\nfunction isPluginFactory(obj: unknown): obj is PluginFactory {\n return (\n typeof obj === 'object' &&\n obj !== null &&\n 'id' in obj &&\n 'pluginType' in obj &&\n 'create' in obj &&\n typeof (obj as PluginFactory).create === 'function'\n );\n}\n\nexport async function loadPlugin<T = unknown>(manifest: PluginManifest): Promise<PluginFactory<T>> {\n let mod: unknown;\n let lastError: unknown;\n\n // 1. Try ESM first\n if (manifest.module) {\n try {\n const esmPath = path.resolve(manifest.__dir, manifest.module);\n const fileUrl = pathToFileURL(esmPath).href;\n // Add timestamp to bust ESM cache for hot-reload\n mod = await import(`${fileUrl}?t=${Date.now()}`);\n } catch (err) {\n lastError = err;\n }\n }\n\n // 2. Fallback to CJS\n if (!mod && manifest.main) {\n try {\n const cjsPath = path.resolve(manifest.__dir, manifest.main);\n // Clear CJS cache before requiring\n delete require.cache[require.resolve(cjsPath)];\n mod = require(cjsPath);\n } catch (err) {\n lastError = err;\n }\n }\n\n if (!mod) {\n throw new PluginLoadError(manifest.id, lastError);\n }\n\n // 3. Normalize export\n const moduleWithDefault = mod as { default?: unknown };\n const factory = (moduleWithDefault.default ?? mod) as PluginFactory<T>;\n\n if (!isPluginFactory(factory)) {\n throw new PluginInvalidExportError(manifest.id);\n }\n\n return factory;\n}\n\n/**\n * Clear the module cache for a plugin (used for hot-reload)\n */\nexport function clearPluginCache(manifest: PluginManifest): void {\n // Clear CJS cache\n if (manifest.main) {\n const cjsPath = path.resolve(manifest.__dir, manifest.main);\n try {\n delete require.cache[require.resolve(cjsPath)];\n } catch {\n // Ignore if not in cache\n }\n }\n\n // ESM cache is handled by timestamp query param in loadPlugin\n}\n","import { FSWatcher, watch } from 'chokidar';\n\nimport { DefaultPluginContext } from './default-plugin-context';\nimport { findPlugins } from './find-plugins';\nimport { topologicalSort } from './helpers';\nimport { loadPlugin, clearPluginCache } from './load-plugin';\nimport {\n ManifestNotFoundError,\n PluginManifest,\n PluginMetadata,\n PluginNotFoundError,\n} from './types';\n\nfunction manifestToMetadata(manifest: PluginManifest): PluginMetadata {\n return {\n id: manifest.id,\n name: manifest.name,\n pluginType: manifest.pluginType,\n version: manifest.version,\n description: manifest.description,\n dependencies: manifest.dependencies,\n dir: manifest.__dir,\n };\n}\n\nexport class PluginManager {\n private readonly context: DefaultPluginContext;\n private manifests: Map<string, PluginManifest> = new Map();\n private watcher: FSWatcher | null = null;\n private watchPatterns: string[] = [];\n\n public constructor() {\n this.context = new DefaultPluginContext();\n }\n\n public async loadPlugins(patterns: string[]): Promise<void> {\n const allManifests: PluginManifest[] = [];\n\n for (const pattern of patterns) {\n const manifests = await findPlugins(pattern);\n allManifests.push(...manifests);\n }\n\n // Sort the manifests so that the dependencies are loaded before their dependents\n const sortedManifests = topologicalSort(allManifests);\n\n for (const manifest of sortedManifests) {\n await this.loadSinglePlugin(manifest);\n }\n }\n\n private async loadSinglePlugin(manifest: PluginManifest): Promise<void> {\n const factory = await loadPlugin(manifest);\n const plugin = await Promise.resolve(factory.create(this.context));\n const metadata = manifestToMetadata(manifest);\n\n this.context.register(manifest.id, plugin, metadata);\n this.manifests.set(manifest.id, manifest);\n\n // Call onLoad lifecycle hook\n if (plugin.onLoad) {\n await Promise.resolve(plugin.onLoad());\n }\n }\n\n private async unloadSinglePlugin(id: string): Promise<void> {\n const plugin = this.context.getPluginInstance(id);\n\n // Call onUnload lifecycle hook before removing\n if (plugin?.onUnload) {\n await Promise.resolve(plugin.onUnload());\n }\n\n this.context.unregister(id);\n this.manifests.delete(id);\n }\n\n /**\n * Reload a specific plugin by ID (useful for hot-reload)\n */\n public async reloadPlugin(id: string): Promise<void> {\n const manifest = this.manifests.get(id);\n if (!manifest) {\n throw new PluginNotFoundError(id);\n }\n\n // Clear the module cache for this plugin\n clearPluginCache(manifest);\n\n // Unload the old plugin\n await this.unloadSinglePlugin(id);\n\n // Re-read the manifest from the plugin directory\n const [newManifest] = await findPlugins(manifest.__dir);\n if (!newManifest) {\n throw new ManifestNotFoundError(id, manifest.__dir);\n }\n\n // Load the new version\n await this.loadSinglePlugin(newManifest);\n }\n\n /**\n * Unload a plugin by ID\n */\n public async unloadPlugin(id: string): Promise<void> {\n if (!this.hasPlugin(id)) {\n throw new PluginNotFoundError(id);\n }\n await this.unloadSinglePlugin(id);\n }\n\n /**\n * Start watching for plugin file changes (hot-reload)\n * @param patterns Glob patterns to watch (same as loadPlugins)\n * @param onChange Callback when a plugin is reloaded\n */\n public async watch(\n patterns: string[],\n onChange?: (id: string, event: 'reload' | 'error', error?: Error) => void\n ): Promise<void> {\n if (this.watcher) {\n await this.stopWatching();\n }\n\n this.watchPatterns = patterns;\n\n // Get all plugin directories to watch\n const dirsToWatch: string[] = [];\n for (const [, manifest] of this.manifests) {\n dirsToWatch.push(manifest.__dir);\n }\n\n if (dirsToWatch.length === 0) {\n return;\n }\n\n this.watcher = watch(dirsToWatch, {\n ignoreInitial: true,\n awaitWriteFinish: {\n stabilityThreshold: 100,\n pollInterval: 50,\n },\n });\n\n this.watcher.on('change', async (filePath: string) => {\n // Find which plugin this file belongs to\n for (const [id, manifest] of this.manifests) {\n if (filePath.startsWith(manifest.__dir)) {\n try {\n await this.reloadPlugin(id);\n onChange?.(id, 'reload');\n } catch (err) {\n onChange?.(\n id,\n 'error',\n err instanceof Error ? err : new Error(String(err))\n );\n }\n break;\n }\n }\n });\n }\n\n /**\n * Stop watching for file changes\n */\n public async stopWatching(): Promise<void> {\n if (this.watcher) {\n await this.watcher.close();\n this.watcher = null;\n }\n }\n\n /**\n * Get a plugin's API by its id\n */\n public getPlugin<T = unknown>(id: string): T {\n return this.context.getPlugin<T>(id);\n }\n\n /**\n * Check if a plugin is loaded\n */\n public hasPlugin(id: string): boolean {\n return this.context.hasPlugin(id);\n }\n\n /**\n * Get all loaded plugin IDs\n */\n public getPluginIds(): string[] {\n return this.context.getPluginIds();\n }\n\n /**\n * Get metadata for a specific plugin\n */\n public getPluginMetadata(id: string): PluginMetadata | undefined {\n return this.context.getPluginMetadata(id);\n }\n\n /**\n * Get all plugins of a specific type\n */\n public getPluginsByType(pluginType: string): PluginMetadata[] {\n return this.context.getPluginsByType(pluginType);\n }\n\n /**\n * Get all loaded plugin metadata\n */\n public getAllMetadata(): PluginMetadata[] {\n return this.context.getAllMetadata();\n }\n\n /**\n * Get the plugin context (for advanced use cases)\n */\n public getContext(): DefaultPluginContext {\n return this.context;\n }\n\n /**\n * Shutdown the plugin manager - unloads all plugins and stops watching\n */\n public async shutdown(): Promise<void> {\n await this.stopWatching();\n\n // Unload plugins in reverse order (dependents before dependencies)\n const ids = [...this.manifests.keys()].reverse();\n for (const id of ids) {\n await this.unloadSinglePlugin(id);\n }\n }\n}\n"],"mappings":"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"}
//#region src/types.d.ts
interface LibriaPlugin<T = unknown> {
interface PluginLifecycle {
/** Called after the plugin is registered and ready to use */
onLoad?(): void | Promise<void>;
/** Called before the plugin is unloaded (for hot-reload or shutdown) */
onUnload?(): void | Promise<void>;
}
interface LibriaPlugin<T = unknown> extends PluginLifecycle {
api: T;
}
interface PluginFactory<T = unknown> {
readonly id: string;
readonly pluginType: string;
readonly name: string;
readonly api: T;
readonly name?: string;
/** Create the plugin instance. Can be sync or async. */
create<C extends PluginContext>(ctx: C): LibriaPlugin<T> | Promise<LibriaPlugin<T>>;
}
interface PluginContext {
getPlugin<T = unknown>(id: string): T;
hasPlugin(id: string): boolean;
}
/** Metadata stored for each loaded plugin */
interface PluginMetadata {
readonly id: string;
readonly name?: string;
readonly pluginType: string;
readonly version: string;
readonly description?: string;
readonly dependencies?: {
id: string;
version: string;
}[];
/** Absolute path to the plugin directory */
readonly dir: string;
}
interface PluginManifest {
readonly name: string;
readonly id: string;
readonly name?: string;
readonly pluginType: string;
readonly version: string;
readonly description?: string;
readonly main?: string;
readonly module?: string;
readonly types?: string;
readonly dependencies?: {
id: string;
version: string;
}[];
readonly __dir: string;

@@ -30,5 +66,53 @@ }

}
declare class DuplicatePluginError extends Error {
readonly id: string;
constructor(id: string);
}
declare class PluginNotFoundError extends Error {
readonly id: string;
constructor(id: string);
}
declare class ManifestNotFoundError extends Error {
readonly pluginId: string;
readonly dir: string;
constructor(pluginId: string, dir: string);
}
declare class CircularDependencyError extends Error {
readonly cycle: string[];
constructor(cycle: string[]);
}
declare class DependencyNotFoundError extends Error {
readonly dependencyId: string;
readonly requestedBy?: string;
constructor(dependencyId: string, requestedBy?: string);
}
declare class VersionMismatchError extends Error {
readonly packageId: string;
readonly actualVersion: string;
readonly requiredVersion: string;
readonly requestedBy?: string;
constructor(packageId: string, actualVersion: string, requiredVersion: string, requestedBy?: string);
}
//#endregion
//#region src/default-plugin-context.d.ts
declare class DefaultPluginContext implements PluginContext {
private plugins;
register(id: string, plugin: LibriaPlugin, metadata: PluginMetadata): void;
unregister(id: string): LibriaPlugin | undefined;
getPlugin<T = unknown>(id: string): T;
hasPlugin(id: string): boolean;
/** Get the raw LibriaPlugin instance (includes lifecycle hooks) */
getPluginInstance(id: string): LibriaPlugin | undefined;
/** Get metadata for a specific plugin */
getPluginMetadata(id: string): PluginMetadata | undefined;
/** Get all plugin IDs */
getPluginIds(): string[];
/** Get all plugins of a specific type */
getPluginsByType(pluginType: string): PluginMetadata[];
/** Get all loaded plugin metadata */
getAllMetadata(): PluginMetadata[];
}
//#endregion
//#region src/define-plugin.d.ts
declare function definePlugin<T>(pluginType: string, name: string, api: T): LibriaPlugin<T>;
declare function definePlugin<T>(factory: PluginFactory<T>): PluginFactory<T>;
//#endregion

@@ -38,9 +122,75 @@ //#region src/find-plugins.d.ts

//#endregion
//#region src/helpers/topological-sort.d.ts
declare function topologicalSort(packages: PluginManifest[]): PluginManifest[];
//#endregion
//#region src/load-plugin.d.ts
declare function loadPlugin<T = unknown>(manifest: PluginManifest): Promise<LibriaPlugin<T>>;
declare function loadPlugin<T = unknown>(manifest: PluginManifest): Promise<PluginFactory<T>>;
/**
* Clear the module cache for a plugin (used for hot-reload)
*/
declare function clearPluginCache(manifest: PluginManifest): void;
//#endregion
//#region src/load-all-plugins.d.ts
declare function loadAllPlugins<T = unknown>(pattern: string, pluginType?: string): Promise<LibriaPlugin<T>[]>;
//#region src/plugin-manager.d.ts
declare class PluginManager {
private readonly context;
private manifests;
private watcher;
private watchPatterns;
constructor();
loadPlugins(patterns: string[]): Promise<void>;
private loadSinglePlugin;
private unloadSinglePlugin;
/**
* Reload a specific plugin by ID (useful for hot-reload)
*/
reloadPlugin(id: string): Promise<void>;
/**
* Unload a plugin by ID
*/
unloadPlugin(id: string): Promise<void>;
/**
* Start watching for plugin file changes (hot-reload)
* @param patterns Glob patterns to watch (same as loadPlugins)
* @param onChange Callback when a plugin is reloaded
*/
watch(patterns: string[], onChange?: (id: string, event: 'reload' | 'error', error?: Error) => void): Promise<void>;
/**
* Stop watching for file changes
*/
stopWatching(): Promise<void>;
/**
* Get a plugin's API by its id
*/
getPlugin<T = unknown>(id: string): T;
/**
* Check if a plugin is loaded
*/
hasPlugin(id: string): boolean;
/**
* Get all loaded plugin IDs
*/
getPluginIds(): string[];
/**
* Get metadata for a specific plugin
*/
getPluginMetadata(id: string): PluginMetadata | undefined;
/**
* Get all plugins of a specific type
*/
getPluginsByType(pluginType: string): PluginMetadata[];
/**
* Get all loaded plugin metadata
*/
getAllMetadata(): PluginMetadata[];
/**
* Get the plugin context (for advanced use cases)
*/
getContext(): DefaultPluginContext;
/**
* Shutdown the plugin manager - unloads all plugins and stops watching
*/
shutdown(): Promise<void>;
}
//#endregion
export { LibriaPlugin, PluginInvalidExportError, PluginLoadError, PluginManifest, PluginTypeMismatchError, definePlugin, findPlugins, loadAllPlugins, loadPlugin };
export { CircularDependencyError, DefaultPluginContext, DependencyNotFoundError, DuplicatePluginError, LibriaPlugin, ManifestNotFoundError, PluginContext, PluginFactory, PluginInvalidExportError, PluginLifecycle, PluginLoadError, PluginManager, PluginManifest, PluginMetadata, PluginNotFoundError, PluginTypeMismatchError, VersionMismatchError, clearPluginCache, definePlugin, findPlugins, loadPlugin, topologicalSort };
//# sourceMappingURL=index.d.cts.map
//#region src/types.d.ts
interface LibriaPlugin<T = unknown> {
interface PluginLifecycle {
/** Called after the plugin is registered and ready to use */
onLoad?(): void | Promise<void>;
/** Called before the plugin is unloaded (for hot-reload or shutdown) */
onUnload?(): void | Promise<void>;
}
interface LibriaPlugin<T = unknown> extends PluginLifecycle {
api: T;
}
interface PluginFactory<T = unknown> {
readonly id: string;
readonly pluginType: string;
readonly name: string;
readonly api: T;
readonly name?: string;
/** Create the plugin instance. Can be sync or async. */
create<C extends PluginContext>(ctx: C): LibriaPlugin<T> | Promise<LibriaPlugin<T>>;
}
interface PluginContext {
getPlugin<T = unknown>(id: string): T;
hasPlugin(id: string): boolean;
}
/** Metadata stored for each loaded plugin */
interface PluginMetadata {
readonly id: string;
readonly name?: string;
readonly pluginType: string;
readonly version: string;
readonly description?: string;
readonly dependencies?: {
id: string;
version: string;
}[];
/** Absolute path to the plugin directory */
readonly dir: string;
}
interface PluginManifest {
readonly name: string;
readonly id: string;
readonly name?: string;
readonly pluginType: string;
readonly version: string;
readonly description?: string;
readonly main?: string;
readonly module?: string;
readonly types?: string;
readonly dependencies?: {
id: string;
version: string;
}[];
readonly __dir: string;

@@ -30,5 +66,53 @@ }

}
declare class DuplicatePluginError extends Error {
readonly id: string;
constructor(id: string);
}
declare class PluginNotFoundError extends Error {
readonly id: string;
constructor(id: string);
}
declare class ManifestNotFoundError extends Error {
readonly pluginId: string;
readonly dir: string;
constructor(pluginId: string, dir: string);
}
declare class CircularDependencyError extends Error {
readonly cycle: string[];
constructor(cycle: string[]);
}
declare class DependencyNotFoundError extends Error {
readonly dependencyId: string;
readonly requestedBy?: string;
constructor(dependencyId: string, requestedBy?: string);
}
declare class VersionMismatchError extends Error {
readonly packageId: string;
readonly actualVersion: string;
readonly requiredVersion: string;
readonly requestedBy?: string;
constructor(packageId: string, actualVersion: string, requiredVersion: string, requestedBy?: string);
}
//#endregion
//#region src/default-plugin-context.d.ts
declare class DefaultPluginContext implements PluginContext {
private plugins;
register(id: string, plugin: LibriaPlugin, metadata: PluginMetadata): void;
unregister(id: string): LibriaPlugin | undefined;
getPlugin<T = unknown>(id: string): T;
hasPlugin(id: string): boolean;
/** Get the raw LibriaPlugin instance (includes lifecycle hooks) */
getPluginInstance(id: string): LibriaPlugin | undefined;
/** Get metadata for a specific plugin */
getPluginMetadata(id: string): PluginMetadata | undefined;
/** Get all plugin IDs */
getPluginIds(): string[];
/** Get all plugins of a specific type */
getPluginsByType(pluginType: string): PluginMetadata[];
/** Get all loaded plugin metadata */
getAllMetadata(): PluginMetadata[];
}
//#endregion
//#region src/define-plugin.d.ts
declare function definePlugin<T>(pluginType: string, name: string, api: T): LibriaPlugin<T>;
declare function definePlugin<T>(factory: PluginFactory<T>): PluginFactory<T>;
//#endregion

@@ -38,9 +122,75 @@ //#region src/find-plugins.d.ts

//#endregion
//#region src/helpers/topological-sort.d.ts
declare function topologicalSort(packages: PluginManifest[]): PluginManifest[];
//#endregion
//#region src/load-plugin.d.ts
declare function loadPlugin<T = unknown>(manifest: PluginManifest): Promise<LibriaPlugin<T>>;
declare function loadPlugin<T = unknown>(manifest: PluginManifest): Promise<PluginFactory<T>>;
/**
* Clear the module cache for a plugin (used for hot-reload)
*/
declare function clearPluginCache(manifest: PluginManifest): void;
//#endregion
//#region src/load-all-plugins.d.ts
declare function loadAllPlugins<T = unknown>(pattern: string, pluginType?: string): Promise<LibriaPlugin<T>[]>;
//#region src/plugin-manager.d.ts
declare class PluginManager {
private readonly context;
private manifests;
private watcher;
private watchPatterns;
constructor();
loadPlugins(patterns: string[]): Promise<void>;
private loadSinglePlugin;
private unloadSinglePlugin;
/**
* Reload a specific plugin by ID (useful for hot-reload)
*/
reloadPlugin(id: string): Promise<void>;
/**
* Unload a plugin by ID
*/
unloadPlugin(id: string): Promise<void>;
/**
* Start watching for plugin file changes (hot-reload)
* @param patterns Glob patterns to watch (same as loadPlugins)
* @param onChange Callback when a plugin is reloaded
*/
watch(patterns: string[], onChange?: (id: string, event: 'reload' | 'error', error?: Error) => void): Promise<void>;
/**
* Stop watching for file changes
*/
stopWatching(): Promise<void>;
/**
* Get a plugin's API by its id
*/
getPlugin<T = unknown>(id: string): T;
/**
* Check if a plugin is loaded
*/
hasPlugin(id: string): boolean;
/**
* Get all loaded plugin IDs
*/
getPluginIds(): string[];
/**
* Get metadata for a specific plugin
*/
getPluginMetadata(id: string): PluginMetadata | undefined;
/**
* Get all plugins of a specific type
*/
getPluginsByType(pluginType: string): PluginMetadata[];
/**
* Get all loaded plugin metadata
*/
getAllMetadata(): PluginMetadata[];
/**
* Get the plugin context (for advanced use cases)
*/
getContext(): DefaultPluginContext;
/**
* Shutdown the plugin manager - unloads all plugins and stops watching
*/
shutdown(): Promise<void>;
}
//#endregion
export { LibriaPlugin, PluginInvalidExportError, PluginLoadError, PluginManifest, PluginTypeMismatchError, definePlugin, findPlugins, loadAllPlugins, loadPlugin };
export { CircularDependencyError, DefaultPluginContext, DependencyNotFoundError, DuplicatePluginError, LibriaPlugin, ManifestNotFoundError, PluginContext, PluginFactory, PluginInvalidExportError, PluginLifecycle, PluginLoadError, PluginManager, PluginManifest, PluginMetadata, PluginNotFoundError, PluginTypeMismatchError, VersionMismatchError, clearPluginCache, definePlugin, findPlugins, loadPlugin, topologicalSort };
//# sourceMappingURL=index.d.mts.map

@@ -1,2 +0,2 @@

import{createRequire as e}from"node:module";import t from"fs-extra";import n from"fast-glob";import r from"path";import{pathToFileURL as i}from"url";function a(e,t,n){return{pluginType:e,name:t,api:n}}async function o(e,i){let a=[];if(e.includes(`*`)||e.includes(`{`)||e.includes(`?`)){let o=await n(e.replace(/\\/g,`/`),{onlyDirectories:!0,absolute:!0});for(let e of o){let n=r.join(e,`plugin.json`);if(!await t.pathExists(n))continue;let o=await t.readJson(n);i&&o.pluginType!==i||a.push({...o,__dir:e})}}else{if(!await t.pathExists(e))return[];let n=await t.readdir(e);for(let o of n){let n=r.join(e,o);if(!(await t.stat(n)).isDirectory())continue;let s=r.join(n,`plugin.json`);if(!await t.pathExists(s))continue;let c=await t.readJson(s);i&&c.pluginType!==i||a.push({...c,__dir:n})}}return a}var s=class extends Error{constructor(e,t){super(`Failed to load plugin "${e}".\n${String(t)}`),this.name=`PluginLoadError`,this.pluginName=e,this.cause=t}},c=class extends Error{constructor(e){super(`Plugin "${e}" has invalid export`),this.name=`PluginInvalidExportError`,this.pluginName=e}},l=class extends Error{constructor(e,t,n){super(`Plugin type mismatch for "${e}": "${n}" !== "${t}"`),this.name=`PluginTypeMismatchError`,this.pluginName=e,this.expected=t,this.actual=n}};const u=e(import.meta.url);async function d(e){let t,n;if(e.module)try{t=await import(i(r.resolve(e.__dir,e.module)).href)}catch(e){n=e}if(!t&&e.main)try{t=u(r.resolve(e.__dir,e.main))}catch(e){n=e}if(!t)throw new s(e.name,n);let a=t.default??t;if(!a||typeof a!=`object`)throw new c(e.name);if(a.pluginType!==e.pluginType)throw new l(e.name,e.pluginType,a.pluginType);return a}async function f(e,t){let n=await o(e,t),r=[];for(let e of n){let t=await d(e);r.push(t)}return r}export{c as PluginInvalidExportError,s as PluginLoadError,l as PluginTypeMismatchError,a as definePlugin,o as findPlugins,f as loadAllPlugins,d as loadPlugin};
import{createRequire as e}from"node:module";import t from"path";import n from"fast-glob";import r from"fs-extra";import i from"semver";import{pathToFileURL as a}from"url";import{watch as o}from"chokidar";var s=class extends Error{constructor(e,t){super(`Failed to load plugin "${e}".\n${String(t)}`),this.name=`PluginLoadError`,this.pluginName=e,this.cause=t}},c=class extends Error{constructor(e){super(`Plugin "${e}" has invalid export`),this.name=`PluginInvalidExportError`,this.pluginName=e}},l=class extends Error{constructor(e,t,n){super(`Plugin type mismatch for "${e}": "${n}" !== "${t}"`),this.name=`PluginTypeMismatchError`,this.pluginName=e,this.expected=t,this.actual=n}},u=class extends Error{constructor(e){super(`Duplicate plugin "${e}"`),this.id=e}},d=class extends Error{constructor(e){super(`Plugin "${e}" not found`),this.name=`PluginNotFoundError`,this.id=e}},f=class extends Error{constructor(e,t){super(`Manifest not found for plugin "${e}" in "${t}"`),this.name=`ManifestNotFoundError`,this.pluginId=e,this.dir=t}},p=class extends Error{constructor(e){super(`Circular dependency detected: ${e.join(` -> `)}`),this.name=`CircularDependencyError`,this.cycle=e}},m=class extends Error{constructor(e,t){let n=t?`Dependency "${e}" not found (required by "${t}")`:`Dependency "${e}" not found`;super(n),this.name=`DependencyNotFoundError`,this.dependencyId=e,this.requestedBy=t}},h=class extends Error{constructor(e,t,n,r){let i=r?`Version mismatch: ${e}@${t} does not satisfy ${n} (required by "${r}")`:`Version mismatch: ${e}@${t} does not satisfy ${n}`;super(i),this.name=`VersionMismatchError`,this.packageId=e,this.actualVersion=t,this.requiredVersion=n,this.requestedBy=r}},g=class{constructor(){this.plugins=new Map}register(e,t,n){if(this.plugins.has(e))throw new u(e);this.plugins.set(e,{plugin:t,metadata:n})}unregister(e){let t=this.plugins.get(e);if(t)return this.plugins.delete(e),t.plugin}getPlugin(e){let t=this.plugins.get(e);if(!t)throw new d(e);return t.plugin.api}hasPlugin(e){return this.plugins.has(e)}getPluginInstance(e){return this.plugins.get(e)?.plugin}getPluginMetadata(e){return this.plugins.get(e)?.metadata}getPluginIds(){return[...this.plugins.keys()]}getPluginsByType(e){let t=[];for(let{metadata:n}of this.plugins.values())n.pluginType===e&&t.push(n);return t}getAllMetadata(){return[...this.plugins.values()].map(e=>e.metadata)}};function _(e){return e}async function v(e,i){let a=[];if(e.includes(`*`)||e.includes(`{`)||e.includes(`?`)){let o=await n(e.replace(/\\/g,`/`),{onlyDirectories:!0,absolute:!0});for(let e of o){let n=t.join(e,`plugin.json`);if(!await r.pathExists(n))continue;let o=await r.readJson(n);i&&o.pluginType!==i||a.push({...o,__dir:e})}}else{if(!await r.pathExists(e))return[];let n=t.join(e,`plugin.json`);if(await r.pathExists(n)){let o=await r.readJson(n);return(!i||o.pluginType===i)&&a.push({...o,__dir:t.resolve(e)}),a}let o=await r.readdir(e);for(let n of o){let o=t.join(e,n);if(!(await r.stat(o)).isDirectory())continue;let s=t.join(o,`plugin.json`);if(!await r.pathExists(s))continue;let c=await r.readJson(s);i&&c.pluginType!==i||a.push({...c,__dir:o})}}return a}function y(e){let t=new Map(e.map(e=>[e.id,e])),n=[],r=new Set,a=new Set;function o(e,s,c=[]){if(a.has(e)){if(s){let n=t.get(e);if(n&&!i.satisfies(n.version,s))throw new h(e,n.version,s,c[c.length-1])}return}if(r.has(e))throw new p([...c,e].slice(c.indexOf(e)));let l=t.get(e);if(!l)throw new m(e,c[c.length-1]);if(s&&!i.satisfies(l.version,s))throw new h(e,l.version,s,c[c.length-1]);if(r.add(e),l.dependencies)for(let t of l.dependencies)o(t.id,t.version,[...c,e]);r.delete(e),a.add(e),n.push(l)}for(let t of e)o(t.id);return n}const b=e(import.meta.url);function x(e){return typeof e==`object`&&!!e&&`id`in e&&`pluginType`in e&&`create`in e&&typeof e.create==`function`}async function S(e){let n,r;if(e.module)try{n=await import(`${a(t.resolve(e.__dir,e.module)).href}?t=${Date.now()}`)}catch(e){r=e}if(!n&&e.main)try{let r=t.resolve(e.__dir,e.main);delete b.cache[b.resolve(r)],n=b(r)}catch(e){r=e}if(!n)throw new s(e.id,r);let i=n.default??n;if(!x(i))throw new c(e.id);return i}function C(e){if(e.main){let n=t.resolve(e.__dir,e.main);try{delete b.cache[b.resolve(n)]}catch{}}}function w(e){return{id:e.id,name:e.name,pluginType:e.pluginType,version:e.version,description:e.description,dependencies:e.dependencies,dir:e.__dir}}var T=class{constructor(){this.manifests=new Map,this.watcher=null,this.watchPatterns=[],this.context=new g}async loadPlugins(e){let t=[];for(let n of e){let e=await v(n);t.push(...e)}let n=y(t);for(let e of n)await this.loadSinglePlugin(e)}async loadSinglePlugin(e){let t=await S(e),n=await Promise.resolve(t.create(this.context)),r=w(e);this.context.register(e.id,n,r),this.manifests.set(e.id,e),n.onLoad&&await Promise.resolve(n.onLoad())}async unloadSinglePlugin(e){let t=this.context.getPluginInstance(e);t?.onUnload&&await Promise.resolve(t.onUnload()),this.context.unregister(e),this.manifests.delete(e)}async reloadPlugin(e){let t=this.manifests.get(e);if(!t)throw new d(e);C(t),await this.unloadSinglePlugin(e);let[n]=await v(t.__dir);if(!n)throw new f(e,t.__dir);await this.loadSinglePlugin(n)}async unloadPlugin(e){if(!this.hasPlugin(e))throw new d(e);await this.unloadSinglePlugin(e)}async watch(e,t){this.watcher&&await this.stopWatching(),this.watchPatterns=e;let n=[];for(let[,e]of this.manifests)n.push(e.__dir);n.length!==0&&(this.watcher=o(n,{ignoreInitial:!0,awaitWriteFinish:{stabilityThreshold:100,pollInterval:50}}),this.watcher.on(`change`,async e=>{for(let[n,r]of this.manifests)if(e.startsWith(r.__dir)){try{await this.reloadPlugin(n),t?.(n,`reload`)}catch(e){t?.(n,`error`,e instanceof Error?e:Error(String(e)))}break}}))}async stopWatching(){this.watcher&&=(await this.watcher.close(),null)}getPlugin(e){return this.context.getPlugin(e)}hasPlugin(e){return this.context.hasPlugin(e)}getPluginIds(){return this.context.getPluginIds()}getPluginMetadata(e){return this.context.getPluginMetadata(e)}getPluginsByType(e){return this.context.getPluginsByType(e)}getAllMetadata(){return this.context.getAllMetadata()}getContext(){return this.context}async shutdown(){await this.stopWatching();let e=[...this.manifests.keys()].reverse();for(let t of e)await this.unloadSinglePlugin(t)}};export{p as CircularDependencyError,g as DefaultPluginContext,m as DependencyNotFoundError,u as DuplicatePluginError,f as ManifestNotFoundError,c as PluginInvalidExportError,s as PluginLoadError,T as PluginManager,d as PluginNotFoundError,l as PluginTypeMismatchError,h as VersionMismatchError,C as clearPluginCache,_ as definePlugin,v as findPlugins,S as loadPlugin,y as topologicalSort};
//# sourceMappingURL=index.mjs.map

@@ -1,1 +0,1 @@

{"version":3,"file":"index.mjs","names":[],"sources":["../src/define-plugin.ts","../src/find-plugins.ts","../src/types.ts","../src/load-plugin.ts","../src/load-all-plugins.ts"],"sourcesContent":["import {LibriaPlugin} from \"./types\";\r\n\r\nexport function definePlugin<T>(\r\n pluginType: string,\r\n name: string,\r\n api: T\r\n): LibriaPlugin<T> {\r\n return {\r\n pluginType: pluginType,\r\n name,\r\n api\r\n };\r\n}","import {PluginManifest} from \"./types\";\r\nimport fs from \"fs-extra\";\r\nimport fg from \"fast-glob\";\r\nimport path from \"path\";\r\n\r\nexport async function findPlugins(\r\n pattern: string,\r\n pluginType?: string\r\n): Promise<PluginManifest[]> {\r\n const manifests: PluginManifest[] = [];\r\n\r\n // Check if pattern is a glob pattern or a simple path\r\n const isGlob = pattern.includes('*') || pattern.includes('{') || pattern.includes('?');\r\n\r\n if (isGlob) {\r\n // Normalize path separators for fast-glob (it expects forward slashes)\r\n const normalizedPattern = pattern.replace(/\\\\/g, '/');\r\n\r\n // Use fast-glob to find all matching directories\r\n const pluginDirs = await fg(normalizedPattern, {\r\n onlyDirectories: true,\r\n absolute: true,\r\n });\r\n\r\n for (const dir of pluginDirs) {\r\n const manifestPath = path.join(dir, 'plugin.json');\r\n if (!(await fs.pathExists(manifestPath))) continue;\r\n\r\n const raw = await fs.readJson(manifestPath);\r\n\r\n if (pluginType && raw.pluginType !== pluginType) continue;\r\n\r\n manifests.push({\r\n ...raw,\r\n __dir: dir\r\n });\r\n }\r\n } else {\r\n // Original behavior for simple directory paths\r\n if (!(await fs.pathExists(pattern))) return [];\r\n\r\n const entries = await fs.readdir(pattern);\r\n\r\n for (const entry of entries) {\r\n const fullPath = path.join(pattern, entry);\r\n const stat = await fs.stat(fullPath);\r\n\r\n if (!stat.isDirectory()) continue;\r\n\r\n const manifestPath = path.join(fullPath, 'plugin.json');\r\n if (!(await fs.pathExists(manifestPath))) continue;\r\n\r\n const raw = await fs.readJson(manifestPath);\r\n\r\n if (pluginType && raw.pluginType !== pluginType) continue;\r\n\r\n manifests.push({\r\n ...raw,\r\n __dir: fullPath\r\n });\r\n }\r\n }\r\n\r\n return manifests;\r\n}","export interface LibriaPlugin<T = unknown> {\r\n readonly pluginType: string;\r\n readonly name: string;\r\n readonly api: T;\r\n}\r\n\r\nexport interface PluginManifest {\r\n readonly name: string;\r\n readonly pluginType: string;\r\n\r\n readonly main?: string; // cjs\r\n readonly module?: string; // esm\r\n readonly types?: string;\r\n\r\n // resolved absolute path to the plugin folder\r\n readonly __dir: string;\r\n}\r\n\r\n// Plugin Errors\r\n\r\nexport class PluginLoadError extends Error {\r\n readonly pluginName: string;\r\n readonly cause: unknown;\r\n\r\n constructor(pluginName: string, cause: unknown) {\r\n super(`Failed to load plugin \"${pluginName}\".\\n${String(cause)}`);\r\n this.name = 'PluginLoadError';\r\n this.pluginName = pluginName;\r\n this.cause = cause;\r\n }\r\n}\r\n\r\nexport class PluginInvalidExportError extends Error {\r\n readonly pluginName: string;\r\n\r\n constructor(pluginName: string) {\r\n super(`Plugin \"${pluginName}\" has invalid export`);\r\n this.name = 'PluginInvalidExportError';\r\n this.pluginName = pluginName;\r\n }\r\n}\r\n\r\nexport class PluginTypeMismatchError extends Error {\r\n readonly pluginName: string;\r\n readonly expected: string;\r\n readonly actual: string;\r\n\r\n constructor(pluginName: string, expected: string, actual: string) {\r\n super(\r\n `Plugin type mismatch for \"${pluginName}\": ` +\r\n `\"${actual}\" !== \"${expected}\"`\r\n );\r\n this.name = 'PluginTypeMismatchError';\r\n this.pluginName = pluginName;\r\n this.expected = expected;\r\n this.actual = actual;\r\n }\r\n}","import {\r\n LibriaPlugin,\r\n PluginManifest,\r\n PluginLoadError,\r\n PluginInvalidExportError,\r\n PluginTypeMismatchError\r\n} from \"./types\";\r\nimport path from \"path\";\r\nimport {pathToFileURL} from \"url\";\r\nimport {createRequire} from \"node:module\";\r\n\r\nconst require = createRequire(import.meta.url);\r\n\r\nexport async function loadPlugin<T = unknown>(\r\n manifest: PluginManifest\r\n): Promise<LibriaPlugin<T>> {\r\n let mod: any;\r\n let lastError: unknown;\r\n\r\n // 1 Try ESM first\r\n if (manifest.module) {\r\n try {\r\n const esmPath = path.resolve(manifest.__dir, manifest.module);\r\n mod = await import(pathToFileURL(esmPath).href);\r\n } catch (err) {\r\n lastError = err;\r\n }\r\n }\r\n\r\n // 2 Fallback to CJS\r\n if (!mod && manifest.main) {\r\n try {\r\n const cjsPath = path.resolve(manifest.__dir, manifest.main);\r\n mod = require(cjsPath);\r\n } catch (err) {\r\n lastError = err;\r\n }\r\n }\r\n\r\n if (!mod) {\r\n throw new PluginLoadError(manifest.name, lastError);\r\n }\r\n\r\n // 3️⃣ Normalize export\r\n const plugin = (mod.default ?? mod) as LibriaPlugin<T>;\r\n\r\n if (!plugin || typeof plugin !== 'object') {\r\n throw new PluginInvalidExportError(manifest.name);\r\n }\r\n\r\n if (plugin.pluginType !== manifest.pluginType) {\r\n throw new PluginTypeMismatchError(\r\n manifest.name,\r\n manifest.pluginType,\r\n plugin.pluginType\r\n );\r\n }\r\n\r\n return plugin;\r\n}","import {LibriaPlugin} from \"./types\";\r\nimport {findPlugins} from \"./find-plugins\";\r\nimport {loadPlugin} from \"./load-plugin\";\r\n\r\nexport async function loadAllPlugins<T = unknown>(\r\n pattern: string,\r\n pluginType?: string\r\n): Promise<LibriaPlugin<T>[]> {\r\n const manifests = await findPlugins(pattern, pluginType);\r\n const plugins: LibriaPlugin<T>[] = [];\r\n\r\n for (const manifest of manifests) {\r\n const plugin = await loadPlugin<T>(manifest);\r\n plugins.push(plugin);\r\n }\r\n\r\n return plugins;\r\n}\r\n"],"mappings":"qJAEA,SAAgB,EACZ,EACA,EACA,EACe,CACf,MAAO,CACS,aACZ,OACA,MACH,CCNL,eAAsB,EAClB,EACA,EACyB,CACzB,IAAM,EAA8B,EAAE,CAKtC,GAFe,EAAQ,SAAS,IAAI,EAAI,EAAQ,SAAS,IAAI,EAAI,EAAQ,SAAS,IAAI,CAE1E,CAKR,IAAM,EAAa,MAAM,EAHC,EAAQ,QAAQ,MAAO,IAAI,CAGN,CAC3C,gBAAiB,GACjB,SAAU,GACb,CAAC,CAEF,IAAK,IAAM,KAAO,EAAY,CAC1B,IAAM,EAAe,EAAK,KAAK,EAAK,cAAc,CAClD,GAAI,CAAE,MAAM,EAAG,WAAW,EAAa,CAAG,SAE1C,IAAM,EAAM,MAAM,EAAG,SAAS,EAAa,CAEvC,GAAc,EAAI,aAAe,GAErC,EAAU,KAAK,CACX,GAAG,EACH,MAAO,EACV,CAAC,MAEH,CAEH,GAAI,CAAE,MAAM,EAAG,WAAW,EAAQ,CAAG,MAAO,EAAE,CAE9C,IAAM,EAAU,MAAM,EAAG,QAAQ,EAAQ,CAEzC,IAAK,IAAM,KAAS,EAAS,CACzB,IAAM,EAAW,EAAK,KAAK,EAAS,EAAM,CAG1C,GAAI,EAFS,MAAM,EAAG,KAAK,EAAS,EAE1B,aAAa,CAAE,SAEzB,IAAM,EAAe,EAAK,KAAK,EAAU,cAAc,CACvD,GAAI,CAAE,MAAM,EAAG,WAAW,EAAa,CAAG,SAE1C,IAAM,EAAM,MAAM,EAAG,SAAS,EAAa,CAEvC,GAAc,EAAI,aAAe,GAErC,EAAU,KAAK,CACX,GAAG,EACH,MAAO,EACV,CAAC,EAIV,OAAO,EC3CX,IAAa,EAAb,cAAqC,KAAM,CAIvC,YAAY,EAAoB,EAAgB,CAC5C,MAAM,0BAA0B,EAAW,MAAM,OAAO,EAAM,GAAG,CACjE,KAAK,KAAO,kBACZ,KAAK,WAAa,EAClB,KAAK,MAAQ,IAIR,EAAb,cAA8C,KAAM,CAGhD,YAAY,EAAoB,CAC5B,MAAM,WAAW,EAAW,sBAAsB,CAClD,KAAK,KAAO,2BACZ,KAAK,WAAa,IAIb,EAAb,cAA6C,KAAM,CAK/C,YAAY,EAAoB,EAAkB,EAAgB,CAC9D,MACI,6BAA6B,EAAW,MACpC,EAAO,SAAS,EAAS,GAChC,CACD,KAAK,KAAO,0BACZ,KAAK,WAAa,EAClB,KAAK,SAAW,EAChB,KAAK,OAAS,IC5CtB,MAAM,EAAU,EAAc,OAAO,KAAK,IAAI,CAE9C,eAAsB,EAClB,EACwB,CACxB,IAAI,EACA,EAGJ,GAAI,EAAS,OACT,GAAI,CAEA,EAAM,MAAM,OAAO,EADH,EAAK,QAAQ,EAAS,MAAO,EAAS,OAAO,CACpB,CAAC,YACrC,EAAK,CACV,EAAY,EAKpB,GAAI,CAAC,GAAO,EAAS,KACjB,GAAI,CAEA,EAAM,EADU,EAAK,QAAQ,EAAS,MAAO,EAAS,KAAK,CACrC,OACjB,EAAK,CACV,EAAY,EAIpB,GAAI,CAAC,EACD,MAAM,IAAI,EAAgB,EAAS,KAAM,EAAU,CAIvD,IAAM,EAAU,EAAI,SAAW,EAE/B,GAAI,CAAC,GAAU,OAAO,GAAW,SAC7B,MAAM,IAAI,EAAyB,EAAS,KAAK,CAGrD,GAAI,EAAO,aAAe,EAAS,WAC/B,MAAM,IAAI,EACN,EAAS,KACT,EAAS,WACT,EAAO,WACV,CAGL,OAAO,ECtDX,eAAsB,EAClB,EACA,EAC0B,CAC1B,IAAM,EAAY,MAAM,EAAY,EAAS,EAAW,CAClD,EAA6B,EAAE,CAErC,IAAK,IAAM,KAAY,EAAW,CAC9B,IAAM,EAAS,MAAM,EAAc,EAAS,CAC5C,EAAQ,KAAK,EAAO,CAGxB,OAAO"}
{"version":3,"file":"index.mjs","names":[],"sources":["../src/types.ts","../src/default-plugin-context.ts","../src/define-plugin.ts","../src/find-plugins.ts","../src/helpers/topological-sort.ts","../src/load-plugin.ts","../src/plugin-manager.ts"],"sourcesContent":["// Lifecycle hooks that plugins can implement\nexport interface PluginLifecycle {\n /** Called after the plugin is registered and ready to use */\n onLoad?(): void | Promise<void>;\n /** Called before the plugin is unloaded (for hot-reload or shutdown) */\n onUnload?(): void | Promise<void>;\n}\n\nexport interface LibriaPlugin<T = unknown> extends PluginLifecycle {\n api: T;\n}\n\nexport interface PluginFactory<T = unknown> {\n readonly id: string;\n readonly pluginType: string;\n readonly name?: string;\n\n /** Create the plugin instance. Can be sync or async. */\n create<C extends PluginContext>(ctx: C): LibriaPlugin<T> | Promise<LibriaPlugin<T>>;\n}\n\nexport interface PluginContext {\n getPlugin<T = unknown>(id: string): T;\n hasPlugin(id: string): boolean;\n}\n\n/** Metadata stored for each loaded plugin */\nexport interface PluginMetadata {\n readonly id: string;\n readonly name?: string;\n readonly pluginType: string;\n readonly version: string;\n readonly description?: string;\n readonly dependencies?: { id: string; version: string }[];\n /** Absolute path to the plugin directory */\n readonly dir: string;\n}\n\nexport interface PluginManifest {\n readonly id: string;\n readonly name?: string;\n readonly pluginType: string;\n readonly version: string; // Semver\n readonly description?: string;\n\n readonly main?: string; // cjs\n readonly module?: string; // esm\n readonly types?: string;\n\n readonly dependencies?: { id: string; version: string }[];\n // resolved absolute path to the plugin folder\n readonly __dir: string;\n}\n\n// Plugin Errors\n\nexport class PluginLoadError extends Error {\n public readonly pluginName: string;\n public readonly cause: unknown;\n\n constructor(pluginName: string, cause: unknown) {\n super(`Failed to load plugin \"${pluginName}\".\\n${String(cause)}`);\n this.name = 'PluginLoadError';\n this.pluginName = pluginName;\n this.cause = cause;\n }\n}\n\nexport class PluginInvalidExportError extends Error {\n public readonly pluginName: string;\n\n constructor(pluginName: string) {\n super(`Plugin \"${pluginName}\" has invalid export`);\n this.name = 'PluginInvalidExportError';\n this.pluginName = pluginName;\n }\n}\n\nexport class PluginTypeMismatchError extends Error {\n public readonly pluginName: string;\n public readonly expected: string;\n public readonly actual: string;\n\n constructor(pluginName: string, expected: string, actual: string) {\n super(`Plugin type mismatch for \"${pluginName}\": ` + `\"${actual}\" !== \"${expected}\"`);\n this.name = 'PluginTypeMismatchError';\n this.pluginName = pluginName;\n this.expected = expected;\n this.actual = actual;\n }\n}\n\nexport class DuplicatePluginError extends Error {\n public readonly id: string;\n\n constructor(id: string) {\n super(`Duplicate plugin \"${id}\"`);\n\n this.id = id;\n }\n}\n\nexport class PluginNotFoundError extends Error {\n public readonly id: string;\n\n constructor(id: string) {\n super(`Plugin \"${id}\" not found`);\n this.name = 'PluginNotFoundError';\n this.id = id;\n }\n}\n\nexport class ManifestNotFoundError extends Error {\n public readonly pluginId: string;\n public readonly dir: string;\n\n constructor(pluginId: string, dir: string) {\n super(`Manifest not found for plugin \"${pluginId}\" in \"${dir}\"`);\n this.name = 'ManifestNotFoundError';\n this.pluginId = pluginId;\n this.dir = dir;\n }\n}\n\n// Dependency Resolution Errors\n\nexport class CircularDependencyError extends Error {\n public readonly cycle: string[];\n\n constructor(cycle: string[]) {\n super(`Circular dependency detected: ${cycle.join(' -> ')}`);\n this.name = 'CircularDependencyError';\n this.cycle = cycle;\n }\n}\n\nexport class DependencyNotFoundError extends Error {\n public readonly dependencyId: string;\n public readonly requestedBy?: string;\n\n constructor(dependencyId: string, requestedBy?: string) {\n const message = requestedBy\n ? `Dependency \"${dependencyId}\" not found (required by \"${requestedBy}\")`\n : `Dependency \"${dependencyId}\" not found`;\n super(message);\n this.name = 'DependencyNotFoundError';\n this.dependencyId = dependencyId;\n this.requestedBy = requestedBy;\n }\n}\n\nexport class VersionMismatchError extends Error {\n public readonly packageId: string;\n public readonly actualVersion: string;\n public readonly requiredVersion: string;\n public readonly requestedBy?: string;\n\n constructor(\n packageId: string,\n actualVersion: string,\n requiredVersion: string,\n requestedBy?: string\n ) {\n const message = requestedBy\n ? `Version mismatch: ${packageId}@${actualVersion} does not satisfy ${requiredVersion} (required by \"${requestedBy}\")`\n : `Version mismatch: ${packageId}@${actualVersion} does not satisfy ${requiredVersion}`;\n super(message);\n this.name = 'VersionMismatchError';\n this.packageId = packageId;\n this.actualVersion = actualVersion;\n this.requiredVersion = requiredVersion;\n this.requestedBy = requestedBy;\n }\n}\n","import {\n DuplicatePluginError,\n LibriaPlugin,\n PluginContext,\n PluginMetadata,\n PluginNotFoundError,\n} from './types';\n\ninterface RegisteredPlugin {\n plugin: LibriaPlugin;\n metadata: PluginMetadata;\n}\n\nexport class DefaultPluginContext implements PluginContext {\n private plugins: Map<string, RegisteredPlugin> = new Map();\n\n public register(id: string, plugin: LibriaPlugin, metadata: PluginMetadata): void {\n if (this.plugins.has(id)) {\n throw new DuplicatePluginError(id);\n }\n\n this.plugins.set(id, { plugin, metadata });\n }\n\n public unregister(id: string): LibriaPlugin | undefined {\n const registered = this.plugins.get(id);\n if (registered) {\n this.plugins.delete(id);\n return registered.plugin;\n }\n return undefined;\n }\n\n public getPlugin<T = unknown>(id: string): T {\n const registered = this.plugins.get(id);\n if (!registered) {\n throw new PluginNotFoundError(id);\n }\n\n return registered.plugin.api as T;\n }\n\n public hasPlugin(id: string): boolean {\n return this.plugins.has(id);\n }\n\n /** Get the raw LibriaPlugin instance (includes lifecycle hooks) */\n public getPluginInstance(id: string): LibriaPlugin | undefined {\n return this.plugins.get(id)?.plugin;\n }\n\n /** Get metadata for a specific plugin */\n public getPluginMetadata(id: string): PluginMetadata | undefined {\n return this.plugins.get(id)?.metadata;\n }\n\n /** Get all plugin IDs */\n public getPluginIds(): string[] {\n return [...this.plugins.keys()];\n }\n\n /** Get all plugins of a specific type */\n public getPluginsByType(pluginType: string): PluginMetadata[] {\n const result: PluginMetadata[] = [];\n for (const { metadata } of this.plugins.values()) {\n if (metadata.pluginType === pluginType) {\n result.push(metadata);\n }\n }\n return result;\n }\n\n /** Get all loaded plugin metadata */\n public getAllMetadata(): PluginMetadata[] {\n return [...this.plugins.values()].map(r => r.metadata);\n }\n}\n","import { PluginFactory } from './types';\n\nexport function definePlugin<T>(factory: PluginFactory<T>): PluginFactory<T> {\n return factory;\n}\n","import path from 'path';\n\nimport fg from 'fast-glob';\nimport fs from 'fs-extra';\n\nimport { PluginManifest } from './types';\n\nexport async function findPlugins(pattern: string, pluginType?: string): Promise<PluginManifest[]> {\n const manifests: PluginManifest[] = [];\n\n // Check if pattern is a glob pattern or a simple path\n const isGlob = pattern.includes('*') || pattern.includes('{') || pattern.includes('?');\n\n if (isGlob) {\n // Normalize path separators for fast-glob (it expects forward slashes)\n const normalizedPattern = pattern.replace(/\\\\/g, '/');\n\n // Use fast-glob to find all matching directories\n const pluginDirs = await fg(normalizedPattern, {\n onlyDirectories: true,\n absolute: true,\n });\n\n for (const dir of pluginDirs) {\n const manifestPath = path.join(dir, 'plugin.json');\n if (!(await fs.pathExists(manifestPath))) continue;\n\n const raw = await fs.readJson(manifestPath);\n\n if (pluginType && raw.pluginType !== pluginType) continue;\n\n manifests.push({\n ...raw,\n __dir: dir,\n });\n }\n } else {\n // Simple directory path - check if it exists\n if (!(await fs.pathExists(pattern))) return [];\n\n // First, check if the directory itself is a plugin (has plugin.json)\n const directManifestPath = path.join(pattern, 'plugin.json');\n if (await fs.pathExists(directManifestPath)) {\n const raw = await fs.readJson(directManifestPath);\n if (!pluginType || raw.pluginType === pluginType) {\n manifests.push({\n ...raw,\n __dir: path.resolve(pattern),\n });\n }\n return manifests;\n }\n\n // Otherwise, scan subdirectories for plugins\n const entries = await fs.readdir(pattern);\n\n for (const entry of entries) {\n const fullPath = path.join(pattern, entry);\n const stat = await fs.stat(fullPath);\n\n if (!stat.isDirectory()) continue;\n\n const manifestPath = path.join(fullPath, 'plugin.json');\n if (!(await fs.pathExists(manifestPath))) continue;\n\n const raw = await fs.readJson(manifestPath);\n\n if (pluginType && raw.pluginType !== pluginType) continue;\n\n manifests.push({\n ...raw,\n __dir: fullPath,\n });\n }\n }\n\n return manifests;\n}\n","import semver from 'semver';\n\nimport {\n CircularDependencyError,\n DependencyNotFoundError,\n PluginManifest,\n VersionMismatchError,\n} from '../types';\n\nexport function topologicalSort(packages: PluginManifest[]): PluginManifest[] {\n const packageMap = new Map(packages.map(pkg => [pkg.id, pkg]));\n const sorted: PluginManifest[] = [];\n const visiting = new Set<string>();\n const visited = new Set<string>();\n\n function visit(id: string, requiredVersion?: string, path: string[] = []): void {\n if (visited.has(id)) {\n // Still need to validate version even if already visited\n if (requiredVersion) {\n const pkg = packageMap.get(id);\n if (pkg && !semver.satisfies(pkg.version, requiredVersion)) {\n throw new VersionMismatchError(\n id,\n pkg.version,\n requiredVersion,\n path[path.length - 1]\n );\n }\n }\n return;\n }\n\n if (visiting.has(id)) {\n // Circular dependency detected\n const cycle = [...path, id].slice(path.indexOf(id));\n throw new CircularDependencyError(cycle);\n }\n\n const pkg = packageMap.get(id);\n if (!pkg) {\n throw new DependencyNotFoundError(id, path[path.length - 1]);\n }\n\n // Validate version requirement\n if (requiredVersion && !semver.satisfies(pkg.version, requiredVersion)) {\n throw new VersionMismatchError(id, pkg.version, requiredVersion, path[path.length - 1]);\n }\n\n visiting.add(id);\n\n // Visit all dependencies first\n if (pkg.dependencies) {\n for (const dep of pkg.dependencies) {\n visit(dep.id, dep.version, [...path, id]);\n }\n }\n\n visiting.delete(id);\n visited.add(id);\n sorted.push(pkg);\n }\n\n // Visit all packages\n for (const pkg of packages) {\n visit(pkg.id);\n }\n\n return sorted;\n}\n","import { createRequire } from 'node:module';\nimport path from 'path';\nimport { pathToFileURL } from 'url';\n\nimport { PluginFactory, PluginInvalidExportError, PluginLoadError, PluginManifest } from './types';\n\nconst require = createRequire(import.meta.url);\n\nfunction isPluginFactory(obj: unknown): obj is PluginFactory {\n return (\n typeof obj === 'object' &&\n obj !== null &&\n 'id' in obj &&\n 'pluginType' in obj &&\n 'create' in obj &&\n typeof (obj as PluginFactory).create === 'function'\n );\n}\n\nexport async function loadPlugin<T = unknown>(manifest: PluginManifest): Promise<PluginFactory<T>> {\n let mod: unknown;\n let lastError: unknown;\n\n // 1. Try ESM first\n if (manifest.module) {\n try {\n const esmPath = path.resolve(manifest.__dir, manifest.module);\n const fileUrl = pathToFileURL(esmPath).href;\n // Add timestamp to bust ESM cache for hot-reload\n mod = await import(`${fileUrl}?t=${Date.now()}`);\n } catch (err) {\n lastError = err;\n }\n }\n\n // 2. Fallback to CJS\n if (!mod && manifest.main) {\n try {\n const cjsPath = path.resolve(manifest.__dir, manifest.main);\n // Clear CJS cache before requiring\n delete require.cache[require.resolve(cjsPath)];\n mod = require(cjsPath);\n } catch (err) {\n lastError = err;\n }\n }\n\n if (!mod) {\n throw new PluginLoadError(manifest.id, lastError);\n }\n\n // 3. Normalize export\n const moduleWithDefault = mod as { default?: unknown };\n const factory = (moduleWithDefault.default ?? mod) as PluginFactory<T>;\n\n if (!isPluginFactory(factory)) {\n throw new PluginInvalidExportError(manifest.id);\n }\n\n return factory;\n}\n\n/**\n * Clear the module cache for a plugin (used for hot-reload)\n */\nexport function clearPluginCache(manifest: PluginManifest): void {\n // Clear CJS cache\n if (manifest.main) {\n const cjsPath = path.resolve(manifest.__dir, manifest.main);\n try {\n delete require.cache[require.resolve(cjsPath)];\n } catch {\n // Ignore if not in cache\n }\n }\n\n // ESM cache is handled by timestamp query param in loadPlugin\n}\n","import { FSWatcher, watch } from 'chokidar';\n\nimport { DefaultPluginContext } from './default-plugin-context';\nimport { findPlugins } from './find-plugins';\nimport { topologicalSort } from './helpers';\nimport { loadPlugin, clearPluginCache } from './load-plugin';\nimport {\n ManifestNotFoundError,\n PluginManifest,\n PluginMetadata,\n PluginNotFoundError,\n} from './types';\n\nfunction manifestToMetadata(manifest: PluginManifest): PluginMetadata {\n return {\n id: manifest.id,\n name: manifest.name,\n pluginType: manifest.pluginType,\n version: manifest.version,\n description: manifest.description,\n dependencies: manifest.dependencies,\n dir: manifest.__dir,\n };\n}\n\nexport class PluginManager {\n private readonly context: DefaultPluginContext;\n private manifests: Map<string, PluginManifest> = new Map();\n private watcher: FSWatcher | null = null;\n private watchPatterns: string[] = [];\n\n public constructor() {\n this.context = new DefaultPluginContext();\n }\n\n public async loadPlugins(patterns: string[]): Promise<void> {\n const allManifests: PluginManifest[] = [];\n\n for (const pattern of patterns) {\n const manifests = await findPlugins(pattern);\n allManifests.push(...manifests);\n }\n\n // Sort the manifests so that the dependencies are loaded before their dependents\n const sortedManifests = topologicalSort(allManifests);\n\n for (const manifest of sortedManifests) {\n await this.loadSinglePlugin(manifest);\n }\n }\n\n private async loadSinglePlugin(manifest: PluginManifest): Promise<void> {\n const factory = await loadPlugin(manifest);\n const plugin = await Promise.resolve(factory.create(this.context));\n const metadata = manifestToMetadata(manifest);\n\n this.context.register(manifest.id, plugin, metadata);\n this.manifests.set(manifest.id, manifest);\n\n // Call onLoad lifecycle hook\n if (plugin.onLoad) {\n await Promise.resolve(plugin.onLoad());\n }\n }\n\n private async unloadSinglePlugin(id: string): Promise<void> {\n const plugin = this.context.getPluginInstance(id);\n\n // Call onUnload lifecycle hook before removing\n if (plugin?.onUnload) {\n await Promise.resolve(plugin.onUnload());\n }\n\n this.context.unregister(id);\n this.manifests.delete(id);\n }\n\n /**\n * Reload a specific plugin by ID (useful for hot-reload)\n */\n public async reloadPlugin(id: string): Promise<void> {\n const manifest = this.manifests.get(id);\n if (!manifest) {\n throw new PluginNotFoundError(id);\n }\n\n // Clear the module cache for this plugin\n clearPluginCache(manifest);\n\n // Unload the old plugin\n await this.unloadSinglePlugin(id);\n\n // Re-read the manifest from the plugin directory\n const [newManifest] = await findPlugins(manifest.__dir);\n if (!newManifest) {\n throw new ManifestNotFoundError(id, manifest.__dir);\n }\n\n // Load the new version\n await this.loadSinglePlugin(newManifest);\n }\n\n /**\n * Unload a plugin by ID\n */\n public async unloadPlugin(id: string): Promise<void> {\n if (!this.hasPlugin(id)) {\n throw new PluginNotFoundError(id);\n }\n await this.unloadSinglePlugin(id);\n }\n\n /**\n * Start watching for plugin file changes (hot-reload)\n * @param patterns Glob patterns to watch (same as loadPlugins)\n * @param onChange Callback when a plugin is reloaded\n */\n public async watch(\n patterns: string[],\n onChange?: (id: string, event: 'reload' | 'error', error?: Error) => void\n ): Promise<void> {\n if (this.watcher) {\n await this.stopWatching();\n }\n\n this.watchPatterns = patterns;\n\n // Get all plugin directories to watch\n const dirsToWatch: string[] = [];\n for (const [, manifest] of this.manifests) {\n dirsToWatch.push(manifest.__dir);\n }\n\n if (dirsToWatch.length === 0) {\n return;\n }\n\n this.watcher = watch(dirsToWatch, {\n ignoreInitial: true,\n awaitWriteFinish: {\n stabilityThreshold: 100,\n pollInterval: 50,\n },\n });\n\n this.watcher.on('change', async (filePath: string) => {\n // Find which plugin this file belongs to\n for (const [id, manifest] of this.manifests) {\n if (filePath.startsWith(manifest.__dir)) {\n try {\n await this.reloadPlugin(id);\n onChange?.(id, 'reload');\n } catch (err) {\n onChange?.(\n id,\n 'error',\n err instanceof Error ? err : new Error(String(err))\n );\n }\n break;\n }\n }\n });\n }\n\n /**\n * Stop watching for file changes\n */\n public async stopWatching(): Promise<void> {\n if (this.watcher) {\n await this.watcher.close();\n this.watcher = null;\n }\n }\n\n /**\n * Get a plugin's API by its id\n */\n public getPlugin<T = unknown>(id: string): T {\n return this.context.getPlugin<T>(id);\n }\n\n /**\n * Check if a plugin is loaded\n */\n public hasPlugin(id: string): boolean {\n return this.context.hasPlugin(id);\n }\n\n /**\n * Get all loaded plugin IDs\n */\n public getPluginIds(): string[] {\n return this.context.getPluginIds();\n }\n\n /**\n * Get metadata for a specific plugin\n */\n public getPluginMetadata(id: string): PluginMetadata | undefined {\n return this.context.getPluginMetadata(id);\n }\n\n /**\n * Get all plugins of a specific type\n */\n public getPluginsByType(pluginType: string): PluginMetadata[] {\n return this.context.getPluginsByType(pluginType);\n }\n\n /**\n * Get all loaded plugin metadata\n */\n public getAllMetadata(): PluginMetadata[] {\n return this.context.getAllMetadata();\n }\n\n /**\n * Get the plugin context (for advanced use cases)\n */\n public getContext(): DefaultPluginContext {\n return this.context;\n }\n\n /**\n * Shutdown the plugin manager - unloads all plugins and stops watching\n */\n public async shutdown(): Promise<void> {\n await this.stopWatching();\n\n // Unload plugins in reverse order (dependents before dependencies)\n const ids = [...this.manifests.keys()].reverse();\n for (const id of ids) {\n await this.unloadSinglePlugin(id);\n }\n }\n}\n"],"mappings":"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"}
{
"name": "@libria/plugin-loader",
"version": "0.1.1",
"version": "2.0.0-alpha",
"description": "Simple plugin loader for Node.js applications",

@@ -37,5 +37,7 @@ "main": "dist/index.cjs",

"dependencies": {
"chokidar": "^5.0.0",
"fast-glob": "^3.3.3",
"fs-extra": "^11.3.3"
"fs-extra": "^11.3.3",
"semver": "^7.7.3"
}
}
+305
-136
# @libria/plugin-loader
A simple, type-safe plugin loader for Node.js applications. Supports both ESM and CommonJS plugins with glob pattern discovery.
A TypeScript-first plugin system for Node.js applications with dependency resolution, lifecycle hooks, and hot-reloading.
## Features
- **Dependency Resolution** - Plugins can depend on other plugins with semver version requirements
- **Circular Dependency Detection** - Throws clear errors when circular dependencies are found
- **Async Plugin Initialization** - Factories can be async for loading configs, connecting to databases, etc.
- **Lifecycle Hooks** - `onLoad` and `onUnload` hooks for setup and cleanup
- **Hot-Reloading** - Watch for file changes and reload plugins on the fly
- **Plugin Queries** - Find plugins by type, get metadata, list all loaded plugins
- **TypeScript First** - Full type safety with generics for plugin APIs
- **ESM & CJS Support** - Load plugins in either module format
## Installation

@@ -13,217 +24,375 @@

### 1. Create a Plugin
### 1. Define a Plugin
Create a `plugin.json` manifest in your plugin directory:
Create a plugin with `definePlugin`:
```typescript
// plugins/greeter/src/index.ts
import { definePlugin } from '@libria/plugin-loader';
interface GreeterAPI {
greet(name: string): string;
}
export default definePlugin<GreeterAPI>({
id: 'greeter',
pluginType: 'util',
create(ctx) {
return {
api: {
greet(name) {
return `Hello, ${name}!`;
}
}
};
}
});
```
### 2. Create a Plugin Manifest
Each plugin needs a `plugin.json` in its directory:
```json
{
"name": "my-plugin",
"pluginType": "greeting",
"module": "./dist/index.mjs"
"id": "greeter",
"name": "Greeter Plugin",
"pluginType": "util",
"version": "1.0.0",
"module": "./dist/index.mjs"
}
```
Create the plugin module:
### 3. Load and Use Plugins
```typescript
// src/index.ts
import { definePlugin } from '@libria/plugin-loader';
import { PluginManager } from '@libria/plugin-loader';
export default definePlugin('greeting', {
sayHello(name: string) {
return `Hello, ${name}!`;
}
});
const manager = new PluginManager();
// Load all plugins from a directory
await manager.loadPlugins(['./plugins/*']);
// Use a plugin
const greeter = manager.getPlugin<GreeterAPI>('greeter');
console.log(greeter.greet('World')); // "Hello, World!"
```
### 2. Load Plugins
## Plugin Manifest
```typescript
import { findPlugins, loadPlugin, loadAllPlugins } from '@libria/plugin-loader';
The `plugin.json` file defines your plugin's metadata:
// Load all plugins from a directory
const plugins = await loadAllPlugins('./plugins', 'greeting');
for (const plugin of plugins) {
console.log(plugin.api.sayHello('World'));
```json
{
"id": "my-plugin",
"name": "My Plugin",
"pluginType": "feature",
"version": "1.0.0",
"description": "Optional description",
"module": "./dist/index.mjs",
"main": "./dist/index.cjs",
"dependencies": [
{ "id": "other-plugin", "version": "^1.0.0" }
]
}
```
## API
| Field | Required | Description |
|-------|----------|-------------|
| `id` | Yes | Unique identifier for the plugin |
| `pluginType` | Yes | Category/type of the plugin (for queries) |
| `version` | Yes | Semver version string |
| `name` | No | Human-readable name |
| `description` | No | Plugin description |
| `module` | No* | Path to ESM entry point |
| `main` | No* | Path to CJS entry point |
| `dependencies` | No | Array of plugin dependencies |
### `definePlugin<T>(pluginType, api, name?)`
*At least one of `module` or `main` is required.
Helper function to create a type-safe plugin export.
## Plugin Dependencies
```typescript
import { definePlugin } from '@libria/plugin-loader';
Plugins can depend on other plugins. Dependencies are loaded first, and the loading order is determined by topological sort.
export default definePlugin('my-type', {
myMethod() {
return 'Hello!';
}
});
```json
{
"id": "plugin-a",
"pluginType": "feature",
"version": "1.0.0",
"module": "./dist/index.mjs",
"dependencies": [
{ "id": "plugin-b", "version": "^1.0.0" },
{ "id": "plugin-c", "version": ">=2.0.0" }
]
}
```
### `findPlugins(pattern, pluginType?)`
Access dependencies in your plugin via the context:
Discovers plugins by scanning directories for `plugin.json` manifests.
```typescript
import { findPlugins } from '@libria/plugin-loader';
export default definePlugin<MyAPI>({
id: 'plugin-a',
pluginType: 'feature',
// Simple directory path (scans one level deep)
const manifests = await findPlugins('./plugins');
create(ctx) {
// Dependencies are guaranteed to be loaded
const pluginB = ctx.getPlugin<PluginBAPI>('plugin-b');
const pluginC = ctx.getPlugin<PluginCAPI>('plugin-c');
// Glob pattern
const manifests = await findPlugins('./plugins/*-plugin');
return {
api: {
doSomething() {
return pluginB.getValue() + pluginC.calculate();
}
}
};
}
});
```
// Recursive glob
const manifests = await findPlugins('./plugins/**/dist');
### Version Requirements
// Filter by plugin type
const manifests = await findPlugins('./plugins', 'greeting');
```
Dependencies use semver ranges:
### `loadPlugin<T>(manifest)`
- `"1.0.0"` - Exact version
- `"^1.0.0"` - Compatible with 1.x.x
- `"~1.0.0"` - Compatible with 1.0.x
- `">=1.0.0 <2.0.0"` - Range
- `"*"` - Any version
Loads a single plugin from its manifest. Supports both ESM (`module`) and CommonJS (`main`) entry points.
## Async Initialization
Plugin factories can be async for loading configs, connecting to services, etc:
```typescript
import { findPlugins, loadPlugin } from '@libria/plugin-loader';
export default definePlugin<DatabaseAPI>({
id: 'database',
pluginType: 'service',
const [manifest] = await findPlugins('./plugins/my-plugin');
const plugin = await loadPlugin<{ sayHello: (name: string) => string }>(manifest);
async create(ctx) {
const config = await loadConfig();
const connection = await connectToDatabase(config);
console.log(plugin.api.sayHello('World'));
return {
api: {
query: (sql) => connection.query(sql),
close: () => connection.close()
}
};
}
});
```
### `loadAllPlugins<T>(pattern, pluginType?)`
## Lifecycle Hooks
Convenience function that combines `findPlugins` and `loadPlugin`. Discovers and loads all matching plugins.
Plugins can implement lifecycle hooks for setup and cleanup:
```typescript
import { loadAllPlugins } from '@libria/plugin-loader';
export default definePlugin<MyAPI>({
id: 'my-plugin',
pluginType: 'feature',
const plugins = await loadAllPlugins<{ greet: () => string }>(
'./plugins/*-plugin',
'greeting'
);
create(ctx) {
let intervalId: NodeJS.Timeout;
for (const plugin of plugins) {
console.log(plugin.api.greet());
}
return {
api: {
// ... your API
},
onLoad() {
// Called after plugin is registered
console.log('Plugin loaded!');
intervalId = setInterval(() => {
console.log('heartbeat');
}, 1000);
},
async onUnload() {
// Called before plugin is unloaded
// Can be async for cleanup
clearInterval(intervalId);
await saveState();
console.log('Plugin unloaded!');
}
};
}
});
```
## Plugin Manifest
## Hot-Reloading
The `plugin.json` file defines plugin metadata:
Reload plugins without restarting your application:
| Field | Type | Required | Description |
|--------------|--------|----------|--------------------------------------|
| `name` | string | Yes | Unique plugin identifier |
| `pluginType` | string | Yes | Plugin category/type for filtering |
| `module` | string | No | ESM entry point (relative path) |
| `main` | string | No | CommonJS entry point (relative path) |
| `types` | string | No | TypeScript declaration file |
```typescript
const manager = new PluginManager();
await manager.loadPlugins(['./plugins/*']);
At least one of `module` or `main` must be specified.
// Manual reload
await manager.reloadPlugin('my-plugin');
### ESM Plugin Example
// Watch for file changes
await manager.watch(['./plugins/*'], (id, event, error) => {
if (event === 'reload') {
console.log(`Plugin ${id} reloaded successfully`);
}
if (event === 'error') {
console.error(`Failed to reload ${id}:`, error);
}
});
// Stop watching
await manager.stopWatching();
```
my-plugin/
plugin.json
dist/
index.mjs
```
```json
{
"name": "my-plugin",
"pluginType": "feature",
"module": "./dist/index.mjs"
}
```
## Plugin Queries
### CommonJS Plugin Example
Query loaded plugins by type or get metadata:
```
my-plugin/
plugin.json
dist/
index.cjs
```
```typescript
// Get all loaded plugin IDs
const ids = manager.getPluginIds();
// ['greeter', 'database', 'logger']
```json
{
"name": "my-plugin",
"pluginType": "feature",
"main": "./dist/index.cjs"
// Check if a plugin is loaded
if (manager.hasPlugin('greeter')) {
// ...
}
```
### Nested Manifest (dist folder)
// Get metadata for a plugin
const meta = manager.getPluginMetadata('greeter');
// { id: 'greeter', name: 'Greeter Plugin', version: '1.0.0', ... }
You can place `plugin.json` inside the `dist` folder and use glob patterns to discover it:
// Get all plugins of a specific type
const services = manager.getPluginsByType('service');
// [{ id: 'database', ... }, { id: 'cache', ... }]
// Get all metadata
const allMeta = manager.getAllMetadata();
```
my-plugin/
dist/
plugin.json
index.mjs
```
## Graceful Shutdown
Properly unload all plugins (calls `onUnload` hooks in reverse order):
```typescript
// Discover plugins with manifest in dist/
const plugins = await loadAllPlugins('./plugins/*/dist');
process.on('SIGTERM', async () => {
await manager.shutdown();
process.exit(0);
});
```
## Types
## Error Handling
### `LibriaPlugin<T>`
The library provides typed errors for common scenarios:
```typescript
interface LibriaPlugin<T = unknown> {
readonly pluginType: string;
readonly name?: string;
readonly api: T;
import {
PluginLoadError, // Failed to load plugin module
PluginInvalidExportError, // Plugin doesn't export a valid factory
PluginNotFoundError, // Plugin not found (getPlugin, reload, unload)
ManifestNotFoundError, // plugin.json not found during reload
DuplicatePluginError, // Attempting to register same ID twice
CircularDependencyError, // Circular dependency detected
DependencyNotFoundError, // Required dependency not found
VersionMismatchError, // Dependency version doesn't match
} from '@libria/plugin-loader';
try {
await manager.loadPlugins(['./plugins/*']);
} catch (err) {
if (err instanceof CircularDependencyError) {
console.error('Circular dependency:', err.cycle.join(' -> '));
}
if (err instanceof VersionMismatchError) {
console.error(
`${err.packageId}@${err.actualVersion} doesn't satisfy ${err.requiredVersion}`
);
}
}
```
### `PluginManifest`
```typescript
interface PluginManifest {
readonly name: string;
readonly pluginType: string;
readonly main?: string;
readonly module?: string;
readonly types?: string;
readonly __dir: string; // Resolved absolute path
try {
await manager.reloadPlugin('my-plugin');
} catch (err) {
if (err instanceof PluginNotFoundError) {
console.error(`Plugin ${err.id} is not loaded`);
}
if (err instanceof ManifestNotFoundError) {
console.error(`Manifest missing for ${err.pluginId} in ${err.dir}`);
}
}
```
## Error Handling
## API Reference
The library throws specific errors for common issues:
### `PluginManager`
- **`PluginLoadError`** - Failed to load the plugin module
- **`PluginInvalidExportError`** - Plugin export is not a valid object
- **`PluginTypeMismatchError`** - Plugin type doesn't match manifest
| Method | Description |
|--------|-------------|
| `loadPlugins(patterns: string[])` | Load plugins from glob patterns |
| `getPlugin<T>(id: string): T` | Get a plugin's API by ID |
| `hasPlugin(id: string): boolean` | Check if a plugin is loaded |
| `getPluginIds(): string[]` | Get all loaded plugin IDs |
| `getPluginMetadata(id: string)` | Get metadata for a plugin |
| `getPluginsByType(type: string)` | Get all plugins of a type |
| `getAllMetadata()` | Get all plugin metadata |
| `reloadPlugin(id: string)` | Hot-reload a specific plugin |
| `unloadPlugin(id: string)` | Unload a specific plugin |
| `watch(patterns, callback)` | Watch for file changes |
| `stopWatching()` | Stop watching for changes |
| `shutdown()` | Unload all plugins and cleanup |
| `getContext()` | Get the internal plugin context |
### `definePlugin<T>(factory: PluginFactory<T>)`
Helper function for defining plugins with proper typing.
### `PluginContext`
Passed to the `create` function:
| Method | Description |
|--------|-------------|
| `getPlugin<T>(id: string): T` | Get another plugin's API |
| `hasPlugin(id: string): boolean` | Check if a plugin is loaded |
### `LibriaPlugin<T>`
The return type of `create`:
```typescript
import { loadPlugin, PluginTypeMismatchError } from '@libria/plugin-loader';
try {
const plugin = await loadPlugin(manifest);
} catch (error) {
if (error instanceof PluginTypeMismatchError) {
console.error(`Type mismatch: expected ${error.expected}, got ${error.actual}`);
}
interface LibriaPlugin<T> {
api: T;
onLoad?(): void | Promise<void>;
onUnload?(): void | Promise<void>;
}
```
## Directory Structure
Recommended project structure:
```
my-app/
├── src/
│ └── index.ts
├── plugins/
│ ├── greeter/
│ │ ├── plugin.json
│ │ ├── src/
│ │ │ └── index.ts
│ │ └── dist/
│ │ └── index.mjs
│ └── database/
│ ├── plugin.json
│ ├── src/
│ │ └── index.ts
│ └── dist/
│ └── index.mjs
└── package.json
```
## License
MIT

Sorry, the diff of this file is not supported yet