-
Notifications
You must be signed in to change notification settings - Fork 34
Expand file tree
/
Copy pathplugin-registry.ts
More file actions
119 lines (101 loc) · 3.27 KB
/
Copy pathplugin-registry.ts
File metadata and controls
119 lines (101 loc) · 3.27 KB
1
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
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
import { logger } from '@veridion/logger';
import type {
AnalysisContext,
IRulePlugin,
PluginConfig,
PluginMetadata,
} from '@veridion/scanner-types';
export class PluginRegistry {
private plugins = new Map<string, IRulePlugin>();
private configs = new Map<string, PluginConfig>();
register(plugin: IRulePlugin, config?: PluginConfig): void {
if (this.plugins.has(plugin.metadata.id)) {
logger.warn({ pluginId: plugin.metadata.id }, 'Plugin already registered, overwriting');
}
if (config) {
this.validateConfig(plugin, config);
this.configs.set(plugin.metadata.id, config);
}
this.plugins.set(plugin.metadata.id, plugin);
logger.info(
{ pluginId: plugin.metadata.id, version: plugin.metadata.version },
'Plugin registered',
);
}
getPluginConfig(pluginId: string): PluginConfig | undefined {
return this.configs.get(pluginId);
}
setPluginConfig(pluginId: string, config: PluginConfig): void {
const plugin = this.plugins.get(pluginId);
if (plugin) {
this.validateConfig(plugin, config);
}
this.configs.set(pluginId, config);
}
private validateConfig(plugin: IRulePlugin, config: PluginConfig): void {
const schema = plugin.metadata.configSchema;
if (!schema) return;
for (const [key, propSchema] of Object.entries(schema)) {
const val = config[key];
if (propSchema.required && (val === undefined || val === null)) {
throw new Error(
`Configuration validation failed for plugin '${plugin.metadata.id}': missing required property '${key}'`,
);
}
if (val !== undefined && val !== null) {
const actualType = Array.isArray(val) ? 'array' : typeof val;
if (actualType !== propSchema.type) {
throw new Error(
`Configuration validation failed for plugin '${plugin.metadata.id}': property '${key}' expected type '${propSchema.type}', got '${actualType}'`,
);
}
}
}
}
registerAll(plugins: IRulePlugin[]): void {
for (const plugin of plugins) {
this.register(plugin);
}
}
unregister(pluginId: string): boolean {
this.configs.delete(pluginId);
return this.plugins.delete(pluginId);
}
get(pluginId: string): IRulePlugin | undefined {
return this.plugins.get(pluginId);
}
getAll(): IRulePlugin[] {
return Array.from(this.plugins.values());
}
getByCategory(category: string): IRulePlugin[] {
return this.getAll().filter(
(p) => p.metadata.category === (category as PluginMetadata['category']),
);
}
getBySeverity(severity: string): IRulePlugin[] {
return this.getAll().filter(
(p) => p.metadata.severity === (severity as PluginMetadata['severity']),
);
}
getByChain(chain: string): IRulePlugin[] {
return this.getAll().filter((p) =>
p.supportsContext({
contractName: '',
sourceCode: '',
chain,
language: '',
compilerVersion: null,
metadata: {},
}),
);
}
getMatchingPlugins(context: AnalysisContext): IRulePlugin[] {
return this.getAll().filter((p) => p.supportsContext(context));
}
getAllMetadata(): PluginMetadata[] {
return this.getAll().map((p) => ({ ...p.metadata }));
}
get size(): number {
return this.plugins.size;
}
}