@semantest/chrome-extension
Version:
Browser extension for ChatGPT-buddy - AI automation extension built on Web-Buddy framework
277 lines (276 loc) • 8.75 kB
JavaScript
/**
* @fileoverview Plugin Context implementation for Web-Buddy plugin system
* @description Provides services and context to plugins
*/
/**
* Default plugin context implementation
*/
export class DefaultPluginContext {
constructor(pluginId, metadata, services) {
this.state = 'uninitialized';
this.dependencies = new Map();
this.pluginId = pluginId;
this.metadata = metadata;
this.contractRegistry = services.contractRegistry;
this.executionService = services.executionService;
this.storageService = services.storageService;
this.tabManager = services.tabManager;
this.extensionAPI = services.extensionAPI;
this.messaging = services.messaging;
this.eventBus = services.eventBus;
this.config = services.config;
this.logger = services.logger;
}
/**
* Create a UI component for the plugin
*/
createUIComponent(definition) {
const componentId = `${this.pluginId}-${definition.name}-${Date.now()}`;
return {
id: componentId,
...definition
};
}
/**
* Create a menu item for the plugin
*/
createMenuItem(definition) {
const menuItemId = `${this.pluginId}-${definition.label.replace(/\s+/g, '-').toLowerCase()}-${Date.now()}`;
return {
id: menuItemId,
...definition
};
}
/**
* Get the current plugin state
*/
getState() {
return this.state;
}
/**
* Set the plugin state
*/
setState(state) {
const oldState = this.state;
this.state = state;
this.logger.debug(`Plugin state changed: ${oldState} -> ${state}`, {
pluginId: this.pluginId,
oldState,
newState: state,
timestamp: new Date().toISOString()
});
}
/**
* Get a dependency instance
*/
async getDependency(dependencyId) {
if (this.dependencies.has(dependencyId)) {
return this.dependencies.get(dependencyId);
}
// Try to resolve dependency dynamically
try {
const dependency = await this.resolveDependency(dependencyId);
if (dependency) {
this.dependencies.set(dependencyId, dependency);
return dependency;
}
}
catch (error) {
this.logger.error(`Failed to resolve dependency ${dependencyId}:`, error, {
pluginId: this.pluginId,
dependencyId
});
}
return null;
}
/**
* Check if a dependency is available
*/
hasDependency(dependencyId) {
return this.dependencies.has(dependencyId);
}
/**
* Add a dependency to the context
*/
addDependency(dependencyId, instance) {
this.dependencies.set(dependencyId, instance);
this.logger.debug(`Dependency added: ${dependencyId}`, {
pluginId: this.pluginId,
dependencyId
});
}
/**
* Remove a dependency from the context
*/
removeDependency(dependencyId) {
this.dependencies.delete(dependencyId);
this.logger.debug(`Dependency removed: ${dependencyId}`, {
pluginId: this.pluginId,
dependencyId
});
}
/**
* Get all available dependencies
*/
getDependencies() {
return Array.from(this.dependencies.keys());
}
/**
* Create a scoped logger for the plugin
*/
createScopedLogger(scope) {
return this.logger.child({
pluginId: this.pluginId,
scope
});
}
/**
* Create a plugin-scoped storage instance
*/
createScopedStorage(namespace) {
return new ScopedStorageService(this.storageService, this.pluginId, namespace);
}
/**
* Get plugin execution metrics
*/
getMetrics() {
return {
pluginId: this.pluginId,
state: this.state,
dependencies: this.dependencies.size,
memoryUsage: this.getMemoryUsage(),
uptime: this.getUptime()
};
}
/**
* Dispose of the context and clean up resources
*/
dispose() {
this.dependencies.clear();
this.logger.info('Plugin context disposed', {
pluginId: this.pluginId
});
}
// Private helper methods
async resolveDependency(dependencyId) {
// This would typically involve looking up the dependency in a service locator
// or dependency injection container. For now, we'll return null.
this.logger.warn(`Cannot resolve dependency: ${dependencyId}`, {
pluginId: this.pluginId,
dependencyId
});
return null;
}
getMemoryUsage() {
// Estimate memory usage (this is a placeholder implementation)
return this.dependencies.size * 1024; // Rough estimate
}
getUptime() {
// Calculate uptime since initialization (placeholder)
return Date.now() - this.metadata.createdAt || 0;
}
}
/**
* Scoped storage service that isolates storage by plugin and namespace
*/
class ScopedStorageService {
constructor(baseStorage, pluginId, namespace) {
this.baseStorage = baseStorage;
this.pluginId = pluginId;
this.namespace = namespace;
this.prefix = `${pluginId}:${namespace}:`;
}
async set(key, value) {
return this.baseStorage.set(this.prefix + key, value);
}
async get(key) {
return this.baseStorage.get(this.prefix + key);
}
async remove(key) {
return this.baseStorage.remove(this.prefix + key);
}
async clear() {
const keys = await this.keys();
for (const key of keys) {
await this.remove(key);
}
}
async keys() {
const allKeys = await this.baseStorage.keys();
return allKeys
.filter(key => key.startsWith(this.prefix))
.map(key => key.substring(this.prefix.length));
}
async setShared(namespace, key, value) {
return this.baseStorage.setShared(namespace, key, value);
}
async getShared(namespace, key) {
return this.baseStorage.getShared(namespace, key);
}
async removeShared(namespace, key) {
return this.baseStorage.removeShared(namespace, key);
}
async getConfig() {
return this.baseStorage.getConfig();
}
async setConfig(config) {
return this.baseStorage.setConfig(config);
}
async migrate(version, migrationFn) {
return this.baseStorage.migrate(version, migrationFn);
}
}
/**
* Plugin context factory for creating configured contexts
*/
export class PluginContextFactory {
constructor(services) {
this.contractRegistry = services.contractRegistry;
this.executionService = services.executionService;
this.baseStorageService = services.storageService;
this.tabManager = services.tabManager;
this.extensionAPI = services.extensionAPI;
this.messaging = services.messaging;
this.eventBus = services.eventBus;
this.loggerFactory = services.loggerFactory;
}
/**
* Create a new plugin context
*/
createContext(pluginId, metadata, config) {
const logger = this.loggerFactory(pluginId);
const storageService = new ScopedStorageService(this.baseStorageService, pluginId, 'default');
return new DefaultPluginContext(pluginId, metadata, {
contractRegistry: this.contractRegistry,
executionService: this.executionService,
storageService,
tabManager: this.tabManager,
extensionAPI: this.extensionAPI,
messaging: this.messaging,
eventBus: this.eventBus,
config,
logger
});
}
/**
* Update factory services
*/
updateServices(services) {
if (services.contractRegistry)
this.contractRegistry = services.contractRegistry;
if (services.executionService)
this.executionService = services.executionService;
if (services.storageService)
this.baseStorageService = services.storageService;
if (services.tabManager)
this.tabManager = services.tabManager;
if (services.extensionAPI)
this.extensionAPI = services.extensionAPI;
if (services.messaging)
this.messaging = services.messaging;
if (services.eventBus)
this.eventBus = services.eventBus;
if (services.loggerFactory)
this.loggerFactory = services.loggerFactory;
}
}