diff --git a/src/plugins/plugin-api.js b/src/plugins/plugin-api.js new file mode 100644 index 0000000..6b89d10 --- /dev/null +++ b/src/plugins/plugin-api.js @@ -0,0 +1,23 @@ +class PluginAPI { + /** + * Called when the plugin is discovered and loaded. + * Receives a scoped context object with APIs. + * @param {object} context - Plugin context (sidebar, commands, settings, etc.) + */ + init(context) { + this.context = context; + } + + /** Called when the plugin is activated (e.g., sidebar panel clicked). */ + activate() {} + + /** Called when the plugin is deactivated. */ + deactivate() {} + + /** Returns the parsed manifest.json for this plugin. */ + getManifest() { + return this._manifest || null; + } +} + +module.exports = { PluginAPI }; diff --git a/tests/plugin-api.test.js b/tests/plugin-api.test.js new file mode 100644 index 0000000..f83eabf --- /dev/null +++ b/tests/plugin-api.test.js @@ -0,0 +1,29 @@ +const { PluginAPI } = require('../src/plugins/plugin-api'); + +describe('PluginAPI', () => { + test('has default no-op lifecycle methods', () => { + const plugin = new PluginAPI(); + expect(() => plugin.init({})).not.toThrow(); + expect(() => plugin.activate()).not.toThrow(); + expect(() => plugin.deactivate()).not.toThrow(); + }); + + test('getManifest returns null by default', () => { + const plugin = new PluginAPI(); + expect(plugin.getManifest()).toBeNull(); + }); + + test('subclass can override init', () => { + let called = false; + class MyPlugin extends PluginAPI { + init(context) { + called = true; + this.context = context; + } + } + const p = new MyPlugin(); + p.init({ foo: 'bar' }); + expect(called).toBe(true); + expect(p.context.foo).toBe('bar'); + }); +});