Pluggable application framework for building extensible desktop-like web applications with plugin architecture support
—
Pending
Does it follow best practices?
Impact
Pending
No eval scenarios have been run
Pending
The risk profile of this skill
Dependency injection system for resolving plugin services with support for both required and optional dependencies. The Application class provides service resolution capabilities through its integrated PluginRegistry.
Resolve services that must be available for the application to function correctly.
/**
* Resolve a required service of a given type.
* @param token - The token for the service type of interest
* @returns A promise which resolves to an instance of the requested service, or rejects if it cannot be resolved
*/
resolveRequiredService<U>(token: Token<U>): Promise<U>;Usage Example:
import { Token } from "@lumino/coreutils";
// Define a service token
interface IDataService {
getData(): Promise<any[]>;
saveData(data: any): Promise<void>;
}
const IDataService = new Token<IDataService>('IDataService');
// Register a plugin that provides the service
app.registerPlugin({
id: 'data-provider',
provides: IDataService,
activate: () => ({
getData: async () => [{ id: 1, name: 'Sample' }],
saveData: async (data) => console.log('Saving:', data)
})
});
// Resolve the required service
try {
const dataService = await app.resolveRequiredService(IDataService);
const data = await dataService.getData();
console.log('Retrieved data:', data);
} catch (error) {
console.error('Failed to resolve required service:', error);
}Resolve services that are optional - the application can function without them.
/**
* Resolve an optional service of a given type.
* @param token - The token for the service type of interest
* @returns A promise which resolves to an instance of the requested service, or null if it cannot be resolved
*/
resolveOptionalService<U>(token: Token<U>): Promise<U | null>;Usage Example:
import { Token } from "@lumino/coreutils";
// Define optional service tokens
interface IThemeService {
setTheme(theme: string): void;
getTheme(): string;
}
interface IAnalyticsService {
track(event: string, data?: any): void;
}
const IThemeService = new Token<IThemeService>('IThemeService');
const IAnalyticsService = new Token<IAnalyticsService>('IAnalyticsService');
// Register plugins that provide optional services
app.registerPlugin({
id: 'theme-provider',
provides: IThemeService,
activate: () => ({
setTheme: (theme: string) => document.body.className = `theme-${theme}`,
getTheme: () => 'default'
})
});
// Resolve optional services
const themeService = await app.resolveOptionalService(IThemeService);
if (themeService) {
themeService.setTheme('dark');
console.log('Theme service available');
} else {
console.log('Theme service not available, using defaults');
}
const analyticsService = await app.resolveOptionalService(IAnalyticsService);
if (analyticsService) {
analyticsService.track('app-started');
} else {
console.log('Analytics service not available');
}Service tokens are used to identify and request specific services. They are created using the Token class from @lumino/coreutils.
interface Token<T> {
readonly name: string;
}
// Token constructor (from @lumino/coreutils)
// new Token<T>(name: string): Token<T>Token Creation Examples:
import { Token } from "@lumino/coreutils";
// Service interface definitions
interface ILoggerService {
log(message: string): void;
error(message: string): void;
warn(message: string): void;
}
interface IConfigService {
get<T>(key: string): T | undefined;
set<T>(key: string, value: T): void;
}
interface INotificationService {
show(message: string, type?: 'info' | 'warning' | 'error'): void;
}
// Create service tokens
const ILoggerService = new Token<ILoggerService>('ILoggerService');
const IConfigService = new Token<IConfigService>('IConfigService');
const INotificationService = new Token<INotificationService>('INotificationService');
// Use tokens for service resolution
const logger = await app.resolveRequiredService(ILoggerService);
const config = await app.resolveOptionalService(IConfigService);
const notifications = await app.resolveOptionalService(INotificationService);Plugins can declare service dependencies that will be automatically resolved when the plugin is activated.
interface IPlugin<T = any, U = any> {
// ... other properties
/** Array of tokens for services this plugin requires */
requires?: Token<any>[];
/** Array of tokens for services this plugin optionally uses */
optional?: Token<any>[];
}Plugin with Service Dependencies:
// Plugin that requires logger and optionally uses config
const myPlugin: IPlugin<Application> = {
id: 'my-feature',
description: 'Feature with service dependencies',
requires: [ILoggerService],
optional: [IConfigService, INotificationService],
activate: (app: Application, logger: ILoggerService, config?: IConfigService, notifications?: INotificationService) => {
logger.log('My feature plugin activated');
// Use required service
logger.log('Initializing feature...');
// Use optional services if available
if (config) {
const setting = config.get<string>('my-feature.setting');
logger.log(`Using setting: ${setting}`);
}
if (notifications) {
notifications.show('Feature activated successfully!', 'info');
}
return {
doSomething: () => logger.log('Doing something...')
};
}
};
app.registerPlugin(myPlugin);Important Behaviors:
Automatic Activation: If a plugin providing a required service hasn't been activated yet, resolving the service will automatically activate that plugin.
Singletons: Services are singletons - the same instance is returned each time a service token is resolved.
Dependency Order: Plugins are activated in dependency order - plugins providing services are activated before plugins that require them.
Circular Dependencies: The plugin system detects and prevents circular dependency chains.
Error Handling: Required service resolution throws an error if the service cannot be provided, while optional service resolution returns null.
Best Practices: