Core APIs
The Qirvo Plugin SDK provides a comprehensive set of APIs for interacting with the Qirvo platform. This reference covers all core APIs available to plugin developers.
Table of Contents
- Plugin Runtime Context
- Base Plugin Class
- Storage API
- HTTP Client API
- Notification API
- Event System
- Logger API
- Configuration API
Plugin Runtime Context
The PluginRuntimeContext is the primary interface between your plugin and the Qirvo platform.
Interface Definition
interface PluginRuntimeContext {
plugin: InstalledPlugin;
config: Record<string, any>;
storage: PluginStorage;
api: PluginAPI;
logger: PluginLogger;
user?: {
id: string;
email?: string;
preferences?: Record<string, any>
};
}
Usage Example
export default class MyPlugin extends BasePlugin {
async onEnable(context: PluginRuntimeContext): Promise<void> {
// Access plugin metadata
console.log(`Plugin ${context.plugin.name} v${context.plugin.version} enabled`);
// Access user information
if (context.user) {
this.log('info', `User ${context.user.email} enabled the plugin`);
}
// Access configuration
const apiKey = context.config.apiKey;
// Use storage
await context.storage.set('lastEnabled', new Date().toISOString());
}
}
Base Plugin Class
The BasePlugin class provides the foundation for all Qirvo plugins with lifecycle hooks and utility methods.
Class Definition
abstract class BasePlugin {
protected context: PluginRuntimeContext;
// Lifecycle hooks
async onInstall?(context: PluginRuntimeContext): Promise<void>;
async onUninstall?(context: PluginRuntimeContext): Promise<void>;
async onEnable?(context: PluginRuntimeContext): Promise<void>;
async onDisable?(context: PluginRuntimeContext): Promise<void>;
async onUpdate?(context: PluginRuntimeContext, oldVersion: string): Promise<void>;
async onConfigChange?(context: PluginRuntimeContext, oldConfig: Record<string, any>): Promise<void>;
// Utility methods
protected log(level: 'info' | 'warn' | 'error' | 'debug', message: string, ...args: any[]): void;
protected async getConfig<T = any>(key?: string): Promise<T>;
protected async setConfig(key: string, value: any): Promise<void>;
protected async getStorage<T = any>(key: string): Promise<T | null>;
protected async setStorage(key: string, value: any): Promise<boolean>;
protected async notify(title: string, message: string, type?: NotificationType): Promise<void>;
protected async httpGet(url: string, options?: RequestInit): Promise<Response>;
protected async httpPost(url: string, data: any, options?: RequestInit): Promise<Response>;
}
Lifecycle Hooks
onInstall
Called when the plugin is first installed.
async onInstall(context: PluginRuntimeContext): Promise<void> {
// Initialize plugin data
await this.setStorage('installDate', new Date().toISOString());
await this.setStorage('version', context.plugin.version);
// Show welcome message
await this.notify('Welcome!', 'Plugin installed successfully', 'success');
// Perform initial setup
await this.initializePlugin();
}
onEnable
Called when the plugin is enabled (including after installation).
async onEnable(context: PluginRuntimeContext): Promise<void> {
// Start services
this.startBackgroundTasks();
// Register event listeners
this.registerEventHandlers();
// Update status
await this.setStorage('enabled', true);
await this.setStorage('lastEnabled', new Date().toISOString());
}
onDisable
Called when the plugin is disabled.
async onDisable(context: PluginRuntimeContext): Promise<void> {
// Stop services
this.stopBackgroundTasks();
// Cleanup resources
this.cleanup();
// Update status
await this.setStorage('enabled', false);
await this.setStorage('lastDisabled', new Date().toISOString());
}
onUpdate
Called when the plugin is updated to a new version.
async onUpdate(context: PluginRuntimeContext, oldVersion: string): Promise<void> {
this.log('info', `Updating from version ${oldVersion} to ${context.plugin.version}`);
// Perform migration if needed
if (this.needsMigration(oldVersion, context.plugin.version)) {
await this.migrateData(oldVersion);
}
// Update stored version
await this.setStorage('version', context.plugin.version);
// Notify user
await this.notify('Updated', `Plugin updated to v${context.plugin.version}`, 'info');
}
onConfigChange
Called when plugin configuration is modified.
async onConfigChange(context: PluginRuntimeContext, oldConfig: Record<string, any>): Promise<void> {
const newConfig = context.config;
// Handle API key changes
if (oldConfig.apiKey !== newConfig.apiKey) {
await this.validateApiKey(newConfig.apiKey);
}
// Handle refresh interval changes
if (oldConfig.refreshInterval !== newConfig.refreshInterval) {
this.updateRefreshInterval(newConfig.refreshInterval);
}
this.log('info', 'Configuration updated', { oldConfig, newConfig });
}
Utility Methods
Logging
// Log levels: 'info', 'warn', 'error', 'debug'
this.log('info', 'Plugin operation completed');
this.log('warn', 'API rate limit approaching');
this.log('error', 'Failed to fetch data', error);
this.log('debug', 'Processing item', { item });
Configuration Management
// Get entire configuration
const config = await this.getConfig();
// Get specific configuration value
const apiKey = await this.getConfig<string>('apiKey');
const refreshInterval = await this.getConfig<number>('refreshInterval');
// Set configuration value (triggers onConfigChange)
await this.setConfig('lastSync', new Date().toISOString());
Storage Operations
// Store data
await this.setStorage('userData', { name: 'John', preferences: {} });
await this.setStorage('cache', responseData);
// Retrieve data
const userData = await this.getStorage<UserData>('userData');
const cache = await this.getStorage('cache');
// Check if data exists
const hasCache = (await this.getStorage('cache')) !== null;
Notifications
// Basic notification
await this.notify('Success', 'Operation completed successfully');
// Typed notifications
await this.notify('Error', 'Failed to save data', 'error');
await this.notify('Warning', 'API quota exceeded', 'warning');
await this.notify('Info', 'Sync in progress', 'info');
await this.notify('Success', 'Data synchronized', 'success');
HTTP Requests
// GET request
const response = await this.httpGet('https://api.example.com/data');
const data = await response.json();
// POST request
const postResponse = await this.httpPost('https://api.example.com/submit', {
name: 'John Doe',
email: 'john@example.com'
});
// With custom headers
const authResponse = await this.httpGet('https://api.example.com/protected', {
headers: {
'Authorization': `Bearer ${apiKey}`,
'Content-Type': 'application/json'
}
});
Storage API
The Storage API provides persistent key-value storage for your plugin.
Interface Definition
interface PluginStorage {
get(key: string): Promise<any>;
set(key: string, value: any): Promise<void>;
delete(key: string): Promise<void>;
clear(): Promise<void>;
keys(): Promise<string[]>;
}
Usage Examples
// Store different data types
await this.context.storage.set('string', 'Hello World');
await this.context.storage.set('number', 42);
await this.context.storage.set('boolean', true);
await this.context.storage.set('object', { name: 'John', age: 30 });
await this.context.storage.set('array', [1, 2, 3, 4, 5]);
// Retrieve data with type safety
const userName = await this.context.storage.get('string') as string;
const userAge = await this.context.storage.get('number') as number;
const isActive = await this.context.storage.get('boolean') as boolean;
const userObj = await this.context.storage.get('object') as { name: string; age: number };
// Handle missing keys
const missingValue = await this.context.storage.get('nonexistent'); // returns null
// Delete specific keys
await this.context.storage.delete('temporary');
// List all keys
const allKeys = await this.context.storage.keys();
console.log('Stored keys:', allKeys);
// Clear all data
await this.context.storage.clear();
Storage Patterns
Caching Pattern
async getCachedData(key: string, fetchFn: () => Promise<any>, ttl: number = 300000): Promise<any> {
const cacheKey = `cache_${key}`;
const timestampKey = `cache_${key}_timestamp`;
// Check if cache exists and is valid
const cached = await this.getStorage(cacheKey);
const timestamp = await this.getStorage<number>(timestampKey);
if (cached && timestamp && (Date.now() - timestamp) < ttl) {
return cached;
}
// Fetch fresh data
const freshData = await fetchFn();
// Store in cache
await this.setStorage(cacheKey, freshData);
await this.setStorage(timestampKey, Date.now());
return freshData;
}
Settings Management
interface PluginSettings {
theme: 'light' | 'dark';
notifications: boolean;
refreshInterval: number;
apiEndpoint: string;
}
async getSettings(): Promise<PluginSettings> {
const settings = await this.getStorage<PluginSettings>('settings');
return {
theme: 'light',
notifications: true,
refreshInterval: 60000,
apiEndpoint: 'https://api.example.com',
...settings
};
}
async updateSettings(updates: Partial<PluginSettings>): Promise<void> {
const currentSettings = await this.getSettings();
const newSettings = { ...currentSettings, ...updates };
await this.setStorage('settings', newSettings);
}
HTTP Client API
The HTTP Client API provides secure, permission-controlled access to external APIs.
Interface Definition
interface PluginHTTPAPI {
get(url: string, options?: RequestInit): Promise<Response>;
post(url: string, data: any, options?: RequestInit): Promise<Response>;
put(url: string, data: any, options?: RequestInit): Promise<Response>;
delete(url: string, options?: RequestInit): Promise<Response>;
request?(url: string, options: RequestInit): Promise<Response>;
}
Usage Examples
Basic Requests
// GET request
const response = await this.context.api.http.get('https://api.example.com/users');
const users = await response.json();
// POST request
const newUser = await this.context.api.http.post('https://api.example.com/users', {
name: 'John Doe',
email: 'john@example.com'
});
// PUT request
const updatedUser = await this.context.api.http.put('https://api.example.com/users/123', {
name: 'Jane Doe'
});
// DELETE request
await this.context.api.http.delete('https://api.example.com/users/123');
Advanced Requests
// With custom headers
const response = await this.context.api.http.get('https://api.example.com/protected', {
headers: {
'Authorization': `Bearer ${apiKey}`,
'X-Custom-Header': 'value'
}
});
// With query parameters
const searchUrl = new URL('https://api.example.com/search');
searchUrl.searchParams.set('q', 'query');
searchUrl.searchParams.set('limit', '10');
const searchResults = await this.context.api.http.get(searchUrl.toString());
// With timeout
const timeoutResponse = await this.context.api.http.get('https://api.example.com/slow', {
signal: AbortSignal.timeout(5000) // 5 second timeout
});
Error Handling
async fetchDataWithRetry(url: string, maxRetries: number = 3): Promise<any> {
for (let attempt = 1; attempt <= maxRetries; attempt++) {
try {
const response = await this.context.api.http.get(url);
if (!response.ok) {
throw new Error(`HTTP ${response.status}: ${response.statusText}`);
}
return await response.json();
} catch (error) {
this.log('warn', `Attempt ${attempt} failed:`, error);
if (attempt === maxRetries) {
this.log('error', 'All retry attempts failed');
throw error;
}
// Exponential backoff
await new Promise(resolve => setTimeout(resolve, Math.pow(2, attempt) * 1000));
}
}
}
Notification API
The Notification API allows plugins to display messages to users.
Interface Definition
interface NotificationAPI {
show(notification: {
title: string;
message: string;
type?: 'info' | 'success' | 'warning' | 'error';
}): void;
schedule?(notification: {
title: string;
message: string;
at: Date;
type?: 'info' | 'success' | 'warning' | 'error';
}): Promise<string>;
cancel?(id: string): Promise<void>;
}
Usage Examples
// Basic notifications
this.context.api.notifications.show({
title: 'Success',
message: 'Data saved successfully',
type: 'success'
});
this.context.api.notifications.show({
title: 'Warning',
message: 'API quota is running low',
type: 'warning'
});
this.context.api.notifications.show({
title: 'Error',
message: 'Failed to connect to server',
type: 'error'
});
// Scheduled notifications (if supported)
if (this.context.api.notifications.schedule) {
const notificationId = await this.context.api.notifications.schedule({
title: 'Reminder',
message: 'Time to sync your data',
at: new Date(Date.now() + 3600000), // 1 hour from now
type: 'info'
});
// Cancel if needed
if (this.context.api.notifications.cancel) {
await this.context.api.notifications.cancel(notificationId);
}
}
Event System
The Event System enables communication between plugins and the Qirvo platform.
Working Plugin Context Events
interface WorkingPluginContext {
bus: {
emit: (event: string, data: any) => void;
on: (event: string, handler: (data: any) => void) => () => void;
};
}
Usage Examples
export default class EventPlugin extends BasePlugin {
private unsubscribers: (() => void)[] = [];
async onEnable(context: PluginRuntimeContext): Promise<void> {
// Subscribe to events
const unsubscribe1 = context.bus.on('user.task.created', this.handleTaskCreated.bind(this));
const unsubscribe2 = context.bus.on('system.sync.completed', this.handleSyncCompleted.bind(this));
this.unsubscribers.push(unsubscribe1, unsubscribe2);
}
async onDisable(): Promise<void> {
// Cleanup event subscriptions
this.unsubscribers.forEach(unsubscribe => unsubscribe());
this.unsubscribers = [];
}
private handleTaskCreated(data: { task: any }): void {
this.log('info', 'New task created:', data.task);
// Handle task creation
}
private handleSyncCompleted(data: { timestamp: string }): void {
this.log('info', 'Sync completed at:', data.timestamp);
// Handle sync completion
}
// Emit custom events
async performAction(): Promise<void> {
// Do something
const result = await this.doSomething();
// Emit event for other plugins
this.context.bus.emit('myplugin.action.completed', {
timestamp: new Date().toISOString(),
result
});
}
}
Logger API
The Logger API provides structured logging capabilities.
Interface Definition
interface PluginLogger {
info(message: string, ...args: any[]): void;
warn(message: string, ...args: any[]): void;
error(message: string, ...args: any[]): void;
debug(message: string, ...args: any[]): void;
}
Usage Examples
// Basic logging
this.context.logger.info('Plugin operation started');
this.context.logger.warn('Deprecated API usage detected');
this.context.logger.error('Failed to process request');
this.context.logger.debug('Processing item', { itemId: 123 });
// Structured logging with context
this.context.logger.info('User action completed', {
userId: this.context.user?.id,
action: 'data_export',
timestamp: new Date().toISOString(),
duration: Date.now() - startTime
});
// Error logging with stack traces
try {
await this.riskyOperation();
} catch (error) {
this.context.logger.error('Operation failed', {
error: error.message,
stack: error.stack,
context: { userId: this.context.user?.id }
});
}
Configuration API
Access and modify plugin configuration through the context.
Usage Examples
// Access configuration
const config = this.context.config;
const apiKey = config.apiKey;
const refreshInterval = config.refreshInterval || 60000;
// Type-safe configuration access
interface MyPluginConfig {
apiKey: string;
refreshInterval: number;
enableNotifications: boolean;
theme: 'light' | 'dark';
}
const typedConfig = this.context.config as MyPluginConfig;
// Configuration validation
private validateConfig(config: any): config is MyPluginConfig {
return (
typeof config.apiKey === 'string' &&
typeof config.refreshInterval === 'number' &&
typeof config.enableNotifications === 'boolean' &&
['light', 'dark'].includes(config.theme)
);
}
async onConfigChange(context: PluginRuntimeContext, oldConfig: Record<string, any>): Promise<void> {
if (!this.validateConfig(context.config)) {
this.log('error', 'Invalid configuration provided');
await this.notify('Configuration Error', 'Please check your plugin settings', 'error');
return;
}
// Handle valid configuration change
const config = context.config as MyPluginConfig;
this.log('info', 'Configuration updated', { config });
}
Next: Plugin Context for detailed context documentation.