PluginLoader Class
The PluginLoader class handles loading 3Lens plugins from various sources including npm packages (via CDN), URLs, and inline definitions.
Overview
import { PluginLoader } from '@3lens/core';
const loader = new PluginLoader();
// Load from npm package
const result = await loader.loadFromNpm('@3lens/plugin-shadows', '1.0.0');
// Load from URL
const result = await loader.loadFromUrl('https://example.com/my-plugin.js');
// Load inline
const result = loader.loadInline(myPluginDefinition);
if (result.success) {
pluginManager.registerPlugin(result.plugin);
await pluginManager.activatePlugin(result.plugin.metadata.id);
}2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
Constructor
new PluginLoader(options?: PluginLoaderOptions)PluginLoaderOptions
interface PluginLoaderOptions {
/** Custom import function (for testing or alternative module systems) */
importFn?: (specifier: string) => Promise<unknown>;
/** Timeout for loading plugins (ms). Default: 30000 */
timeout?: number;
/** Whether to allow loading from URLs. Default: true */
allowUrlImports?: boolean;
/** CDN URL template for npm packages. Default: unpkg */
cdnTemplate?: string;
}2
3
4
5
6
7
8
9
10
11
12
13
Default CDN Template:
https://unpkg.com/{package}@{version}/dist/index.jsExample:
const loader = new PluginLoader({
timeout: 60000,
allowUrlImports: false, // Security: disable URL imports
cdnTemplate: 'https://cdn.jsdelivr.net/npm/{package}@{version}/dist/index.js',
});2
3
4
5
Loading Methods
loadFromNpm
Loads a plugin from an npm package via CDN.
async loadFromNpm(
packageName: string,
version?: string,
options?: Record<string, unknown>
): Promise<PluginLoadResult>2
3
4
5
Parameters:
| Parameter | Type | Default | Description |
|---|---|---|---|
packageName | string | - | npm package name |
version | string | 'latest' | Version constraint |
options | Record<string, unknown> | undefined | Options passed to plugin factory |
Example:
// Load latest version
const result = await loader.loadFromNpm('@3lens/plugin-shadows');
// Load specific version
const result = await loader.loadFromNpm('@3lens/plugin-shadows', '1.2.3');
// Load with options
const result = await loader.loadFromNpm('@3lens/plugin-shadows', '1.0.0', {
enableAdvancedFeatures: true,
});
if (result.success) {
console.log(`Loaded: ${result.plugin.metadata.name}`);
console.log(`Load time: ${result.loadTime}ms`);
} else {
console.error('Failed to load:', result.error);
}2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
loadFromUrl
Loads a plugin from a URL.
async loadFromUrl(
url: string,
options?: Record<string, unknown>
): Promise<PluginLoadResult>2
3
4
Parameters:
| Parameter | Type | Description |
|---|---|---|
url | string | URL to the plugin module |
options | Record<string, unknown> | Options passed to plugin factory |
Example:
const result = await loader.loadFromUrl(
'https://my-cdn.com/plugins/custom-analyzer.js'
);
if (result.success) {
pluginManager.registerPlugin(result.plugin);
}2
3
4
5
6
7
WARNING
URL imports can be disabled via allowUrlImports: false for security.
loadInline
Loads an inline plugin definition (already in code).
loadInline(plugin: DevtoolPlugin): PluginLoadResultParameters:
| Parameter | Type | Description |
|---|---|---|
plugin | DevtoolPlugin | The plugin definition |
Returns: PluginLoadResult (synchronous)
Example:
const myPlugin: DevtoolPlugin = {
metadata: {
id: 'my-inline-plugin',
name: 'My Inline Plugin',
version: '1.0.0',
},
activate(context) {
context.log('Activated!');
},
};
const result = loader.loadInline(myPlugin);
if (result.success) {
pluginManager.registerPlugin(result.plugin);
}2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
loadFromSource
Loads a plugin from a source configuration.
async loadFromSource(source: PluginSource): Promise<PluginLoadResult>Example:
const result = await loader.loadFromSource({
type: 'npm',
source: '@3lens/plugin-shadows',
version: '1.0.0',
options: { debug: true },
autoActivate: true,
});2
3
4
5
6
7
loadMultiple
Loads multiple plugins from source configurations.
async loadMultiple(sources: PluginSource[]): Promise<PluginLoadResult[]>Example:
const results = await loader.loadMultiple([
{ type: 'npm', source: '@3lens/plugin-shadows', version: '1.0.0' },
{ type: 'npm', source: '@3lens/plugin-lod', version: '1.0.0' },
{ type: 'url', source: 'https://example.com/custom-plugin.js' },
]);
const successful = results.filter(r => r.success);
const failed = results.filter(r => !r.success);
console.log(`Loaded ${successful.length} plugins, ${failed.length} failed`);2
3
4
5
6
7
8
9
10
Query Methods
getLoadedPlugin
Gets a loaded plugin by ID.
getLoadedPlugin(id: PluginId): DevtoolPlugin | undefinedgetLoadedPlugins
Gets all loaded plugins.
getLoadedPlugins(): DevtoolPlugin[]isLoaded
Checks if a plugin is loaded.
isLoaded(id: PluginId): booleanManagement Methods
unload
Unloads a plugin from the loader's cache.
unload(id: PluginId): booleanWARNING
This only removes the plugin from the loader's cache. To fully remove a plugin, also call pluginManager.unregisterPlugin().
clear
Clears all loaded plugins and pending loads.
clear(): voidTypes
PluginSource
Configuration for a plugin source.
interface PluginSource {
/** Source type */
type: PluginSourceType; // 'npm' | 'url' | 'inline' | 'local'
/** Package name (for npm), URL (for url), or path (for local) */
source: string;
/** Optional version constraint (for npm) */
version?: string;
/** Optional configuration to pass to the plugin factory */
options?: Record<string, unknown>;
/** Whether to auto-activate after loading */
autoActivate?: boolean;
}2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
PluginLoadResult
Result of a plugin load operation.
interface PluginLoadResult {
/** Whether loading succeeded */
success: boolean;
/** The loaded plugin (if successful) */
plugin?: DevtoolPlugin;
/** Error (if failed) */
error?: Error;
/** Source configuration */
source: PluginSource;
/** Load time in milliseconds */
loadTime: number;
}2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
PluginPackage
Expected format for plugin npm packages.
interface PluginPackage {
/** The plugin definition */
plugin: DevtoolPlugin;
/** Optional factory function to create the plugin with options */
createPlugin?: (options?: Record<string, unknown>) => DevtoolPlugin;
}2
3
4
5
6
7
Plugin Package Format
When publishing a plugin as an npm package, export it in one of these formats:
Named Export
// index.ts
export const plugin: DevtoolPlugin = {
metadata: { id: 'my-plugin', name: 'My Plugin', version: '1.0.0' },
activate(context) { /* ... */ },
};2
3
4
5
Factory Function
// index.ts
export function createPlugin(options?: MyPluginOptions): DevtoolPlugin {
return {
metadata: { id: 'my-plugin', name: 'My Plugin', version: '1.0.0' },
activate(context) {
// Use options
if (options?.enableFeature) {
// ...
}
},
};
}2
3
4
5
6
7
8
9
10
11
12
Default Export
// index.ts
const plugin: DevtoolPlugin = {
metadata: { id: 'my-plugin', name: 'My Plugin', version: '1.0.0' },
activate(context) { /* ... */ },
};
export default plugin;2
3
4
5
6
7
Complete Example
import { createProbe, PluginLoader } from '@3lens/core';
const probe = createProbe({ name: 'My App' });
const pluginManager = probe.getPluginManager();
const loader = new PluginLoader({
timeout: 30000,
cdnTemplate: 'https://unpkg.com/{package}@{version}/dist/index.js',
});
// Define plugin sources
const pluginSources: PluginSource[] = [
// Official plugins from npm
{ type: 'npm', source: '@3lens/plugin-shadows', version: '^1.0.0' },
{ type: 'npm', source: '@3lens/plugin-performance', version: 'latest' },
// Custom plugin from URL
{ type: 'url', source: 'https://my-cdn.com/plugins/custom-viz.js' },
];
async function loadAndActivatePlugins() {
console.log('Loading plugins...');
const results = await loader.loadMultiple(pluginSources);
for (const result of results) {
if (result.success && result.plugin) {
console.log(`✓ Loaded: ${result.plugin.metadata.name} (${result.loadTime}ms)`);
// Register with plugin manager
pluginManager.registerPlugin(result.plugin);
// Activate
try {
await pluginManager.activatePlugin(result.plugin.metadata.id);
console.log(` ✓ Activated`);
} catch (error) {
console.error(` ✗ Activation failed:`, error);
}
} else {
console.error(`✗ Failed to load from ${result.source.source}:`, result.error);
}
}
// Summary
const loaded = results.filter(r => r.success).length;
console.log(`\nLoaded ${loaded}/${results.length} plugins`);
}
// Also support loading plugins dynamically
async function loadPluginByName(packageName: string) {
const result = await loader.loadFromNpm(packageName);
if (result.success && result.plugin) {
pluginManager.registerPlugin(result.plugin);
await pluginManager.activatePlugin(result.plugin.metadata.id);
return result.plugin;
}
throw result.error ?? new Error('Unknown error');
}
// Start
loadAndActivatePlugins();2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
Security Considerations
Disable URL imports in production if you only want to allow vetted plugins:
typescriptnew PluginLoader({ allowUrlImports: false });1Use specific versions instead of
latestfor production:typescriptloader.loadFromNpm('@3lens/plugin', '1.2.3'); // Good loader.loadFromNpm('@3lens/plugin', 'latest'); // Risky1
2Validate plugin sources before loading user-provided URLs.
Set reasonable timeouts to prevent hanging on slow/malicious endpoints:
typescriptnew PluginLoader({ timeout: 10000 });1
See Also
- DevtoolPlugin - Plugin interface
- PluginManager - Plugin lifecycle management
- Plugin Development Guide - Creating plugins