@zintrust/workers 0.1.27

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (178) hide show
  1. package/README.md +861 -0
  2. package/dist/AnomalyDetection.d.ts +102 -0
  3. package/dist/AnomalyDetection.js +321 -0
  4. package/dist/AutoScaler.d.ts +127 -0
  5. package/dist/AutoScaler.js +425 -0
  6. package/dist/BroadcastWorker.d.ts +21 -0
  7. package/dist/BroadcastWorker.js +24 -0
  8. package/dist/CanaryController.d.ts +103 -0
  9. package/dist/CanaryController.js +380 -0
  10. package/dist/ChaosEngineering.d.ts +79 -0
  11. package/dist/ChaosEngineering.js +216 -0
  12. package/dist/CircuitBreaker.d.ts +106 -0
  13. package/dist/CircuitBreaker.js +374 -0
  14. package/dist/ClusterLock.d.ts +90 -0
  15. package/dist/ClusterLock.js +385 -0
  16. package/dist/ComplianceManager.d.ts +177 -0
  17. package/dist/ComplianceManager.js +556 -0
  18. package/dist/DatacenterOrchestrator.d.ts +133 -0
  19. package/dist/DatacenterOrchestrator.js +404 -0
  20. package/dist/DeadLetterQueue.d.ts +122 -0
  21. package/dist/DeadLetterQueue.js +539 -0
  22. package/dist/HealthMonitor.d.ts +42 -0
  23. package/dist/HealthMonitor.js +301 -0
  24. package/dist/MultiQueueWorker.d.ts +89 -0
  25. package/dist/MultiQueueWorker.js +277 -0
  26. package/dist/NotificationWorker.d.ts +21 -0
  27. package/dist/NotificationWorker.js +23 -0
  28. package/dist/Observability.d.ts +153 -0
  29. package/dist/Observability.js +530 -0
  30. package/dist/PluginManager.d.ts +123 -0
  31. package/dist/PluginManager.js +392 -0
  32. package/dist/PriorityQueue.d.ts +117 -0
  33. package/dist/PriorityQueue.js +244 -0
  34. package/dist/ResourceMonitor.d.ts +164 -0
  35. package/dist/ResourceMonitor.js +605 -0
  36. package/dist/SLAMonitor.d.ts +110 -0
  37. package/dist/SLAMonitor.js +274 -0
  38. package/dist/WorkerFactory.d.ts +193 -0
  39. package/dist/WorkerFactory.js +1507 -0
  40. package/dist/WorkerInit.d.ts +85 -0
  41. package/dist/WorkerInit.js +223 -0
  42. package/dist/WorkerMetrics.d.ts +114 -0
  43. package/dist/WorkerMetrics.js +509 -0
  44. package/dist/WorkerRegistry.d.ts +145 -0
  45. package/dist/WorkerRegistry.js +319 -0
  46. package/dist/WorkerShutdown.d.ts +61 -0
  47. package/dist/WorkerShutdown.js +159 -0
  48. package/dist/WorkerVersioning.d.ts +107 -0
  49. package/dist/WorkerVersioning.js +300 -0
  50. package/dist/build-manifest.json +462 -0
  51. package/dist/config/workerConfig.d.ts +3 -0
  52. package/dist/config/workerConfig.js +19 -0
  53. package/dist/createQueueWorker.d.ts +23 -0
  54. package/dist/createQueueWorker.js +113 -0
  55. package/dist/dashboard/index.d.ts +1 -0
  56. package/dist/dashboard/index.js +1 -0
  57. package/dist/dashboard/types.d.ts +117 -0
  58. package/dist/dashboard/types.js +1 -0
  59. package/dist/dashboard/workers-api.d.ts +4 -0
  60. package/dist/dashboard/workers-api.js +638 -0
  61. package/dist/dashboard/workers-dashboard-ui.d.ts +3 -0
  62. package/dist/dashboard/workers-dashboard-ui.js +1026 -0
  63. package/dist/dashboard/workers-dashboard.d.ts +4 -0
  64. package/dist/dashboard/workers-dashboard.js +904 -0
  65. package/dist/helper/index.d.ts +5 -0
  66. package/dist/helper/index.js +10 -0
  67. package/dist/http/WorkerApiController.d.ts +38 -0
  68. package/dist/http/WorkerApiController.js +312 -0
  69. package/dist/http/WorkerController.d.ts +374 -0
  70. package/dist/http/WorkerController.js +1351 -0
  71. package/dist/http/middleware/CustomValidation.d.ts +92 -0
  72. package/dist/http/middleware/CustomValidation.js +270 -0
  73. package/dist/http/middleware/DatacenterValidator.d.ts +3 -0
  74. package/dist/http/middleware/DatacenterValidator.js +94 -0
  75. package/dist/http/middleware/EditWorkerValidation.d.ts +7 -0
  76. package/dist/http/middleware/EditWorkerValidation.js +55 -0
  77. package/dist/http/middleware/FeaturesValidator.d.ts +3 -0
  78. package/dist/http/middleware/FeaturesValidator.js +60 -0
  79. package/dist/http/middleware/InfrastructureValidator.d.ts +31 -0
  80. package/dist/http/middleware/InfrastructureValidator.js +226 -0
  81. package/dist/http/middleware/OptionsValidator.d.ts +3 -0
  82. package/dist/http/middleware/OptionsValidator.js +112 -0
  83. package/dist/http/middleware/PayloadSanitizer.d.ts +7 -0
  84. package/dist/http/middleware/PayloadSanitizer.js +42 -0
  85. package/dist/http/middleware/ProcessorPathSanitizer.d.ts +3 -0
  86. package/dist/http/middleware/ProcessorPathSanitizer.js +74 -0
  87. package/dist/http/middleware/QueueNameSanitizer.d.ts +3 -0
  88. package/dist/http/middleware/QueueNameSanitizer.js +45 -0
  89. package/dist/http/middleware/ValidateDriver.d.ts +7 -0
  90. package/dist/http/middleware/ValidateDriver.js +20 -0
  91. package/dist/http/middleware/VersionSanitizer.d.ts +3 -0
  92. package/dist/http/middleware/VersionSanitizer.js +25 -0
  93. package/dist/http/middleware/WorkerNameSanitizer.d.ts +3 -0
  94. package/dist/http/middleware/WorkerNameSanitizer.js +46 -0
  95. package/dist/http/middleware/WorkerValidationChain.d.ts +27 -0
  96. package/dist/http/middleware/WorkerValidationChain.js +185 -0
  97. package/dist/index.d.ts +46 -0
  98. package/dist/index.js +48 -0
  99. package/dist/routes/workers.d.ts +12 -0
  100. package/dist/routes/workers.js +81 -0
  101. package/dist/storage/WorkerStore.d.ts +45 -0
  102. package/dist/storage/WorkerStore.js +195 -0
  103. package/dist/type.d.ts +76 -0
  104. package/dist/type.js +1 -0
  105. package/dist/ui/router/ui.d.ts +3 -0
  106. package/dist/ui/router/ui.js +83 -0
  107. package/dist/ui/types/worker-ui.d.ts +229 -0
  108. package/dist/ui/types/worker-ui.js +5 -0
  109. package/package.json +53 -0
  110. package/src/AnomalyDetection.ts +434 -0
  111. package/src/AutoScaler.ts +654 -0
  112. package/src/BroadcastWorker.ts +34 -0
  113. package/src/CanaryController.ts +531 -0
  114. package/src/ChaosEngineering.ts +301 -0
  115. package/src/CircuitBreaker.ts +495 -0
  116. package/src/ClusterLock.ts +499 -0
  117. package/src/ComplianceManager.ts +815 -0
  118. package/src/DatacenterOrchestrator.ts +561 -0
  119. package/src/DeadLetterQueue.ts +733 -0
  120. package/src/HealthMonitor.ts +390 -0
  121. package/src/MultiQueueWorker.ts +431 -0
  122. package/src/NotificationWorker.ts +33 -0
  123. package/src/Observability.ts +696 -0
  124. package/src/PluginManager.ts +551 -0
  125. package/src/PriorityQueue.ts +351 -0
  126. package/src/ResourceMonitor.ts +769 -0
  127. package/src/SLAMonitor.ts +408 -0
  128. package/src/WorkerFactory.ts +2108 -0
  129. package/src/WorkerInit.ts +313 -0
  130. package/src/WorkerMetrics.ts +709 -0
  131. package/src/WorkerRegistry.ts +443 -0
  132. package/src/WorkerShutdown.ts +210 -0
  133. package/src/WorkerVersioning.ts +422 -0
  134. package/src/config/workerConfig.ts +25 -0
  135. package/src/createQueueWorker.ts +174 -0
  136. package/src/dashboard/index.ts +6 -0
  137. package/src/dashboard/types.ts +141 -0
  138. package/src/dashboard/workers-api.ts +785 -0
  139. package/src/dashboard/zintrust.svg +30 -0
  140. package/src/helper/index.ts +11 -0
  141. package/src/http/WorkerApiController.ts +369 -0
  142. package/src/http/WorkerController.ts +1512 -0
  143. package/src/http/middleware/CustomValidation.ts +360 -0
  144. package/src/http/middleware/DatacenterValidator.ts +124 -0
  145. package/src/http/middleware/EditWorkerValidation.ts +74 -0
  146. package/src/http/middleware/FeaturesValidator.ts +82 -0
  147. package/src/http/middleware/InfrastructureValidator.ts +295 -0
  148. package/src/http/middleware/OptionsValidator.ts +144 -0
  149. package/src/http/middleware/PayloadSanitizer.ts +52 -0
  150. package/src/http/middleware/ProcessorPathSanitizer.ts +86 -0
  151. package/src/http/middleware/QueueNameSanitizer.ts +55 -0
  152. package/src/http/middleware/ValidateDriver.ts +29 -0
  153. package/src/http/middleware/VersionSanitizer.ts +30 -0
  154. package/src/http/middleware/WorkerNameSanitizer.ts +56 -0
  155. package/src/http/middleware/WorkerValidationChain.ts +230 -0
  156. package/src/index.ts +98 -0
  157. package/src/routes/workers.ts +154 -0
  158. package/src/storage/WorkerStore.ts +240 -0
  159. package/src/type.ts +89 -0
  160. package/src/types/queue-monitor.d.ts +38 -0
  161. package/src/types/queue-redis.d.ts +38 -0
  162. package/src/ui/README.md +13 -0
  163. package/src/ui/components/JsonEditor.js +670 -0
  164. package/src/ui/components/JsonViewer.js +387 -0
  165. package/src/ui/components/WorkerCard.js +178 -0
  166. package/src/ui/components/WorkerExpandPanel.js +257 -0
  167. package/src/ui/components/fetcher.js +42 -0
  168. package/src/ui/components/sla-scorecard.js +32 -0
  169. package/src/ui/components/styles.css +30 -0
  170. package/src/ui/components/table-expander.js +34 -0
  171. package/src/ui/integration/worker-ui-integration.js +565 -0
  172. package/src/ui/router/ui.ts +99 -0
  173. package/src/ui/services/workerApi.js +240 -0
  174. package/src/ui/types/worker-ui.ts +283 -0
  175. package/src/ui/utils/jsonValidator.js +444 -0
  176. package/src/ui/workers/index.html +202 -0
  177. package/src/ui/workers/main.js +1781 -0
  178. package/src/ui/workers/styles.css +1350 -0
@@ -0,0 +1,551 @@
1
+ /**
2
+ * Plugin Manager
3
+ * Extensible plugin system with lifecycle hooks
4
+ * Sealed namespace for immutability
5
+ */
6
+
7
+ import { ErrorFactory, Logger } from '@zintrust/core';
8
+
9
+ export type PluginHook =
10
+ | 'beforeStart'
11
+ | 'afterStart'
12
+ | 'beforeProcess'
13
+ | 'afterProcess'
14
+ | 'beforeStop'
15
+ | 'afterStop'
16
+ | 'onError'
17
+ | 'onComplete'
18
+ | 'onRetry'
19
+ | 'onCircuitOpen'
20
+ | 'onCircuitClose'
21
+ | 'onScaleUp'
22
+ | 'onScaleDown';
23
+
24
+ export type HookContext = {
25
+ workerName: string;
26
+ jobId?: string;
27
+ version?: string;
28
+ jobData?: unknown;
29
+ error?: Error;
30
+ metadata?: Record<string, unknown>;
31
+ timestamp: Date;
32
+ };
33
+
34
+ export type HookResult = {
35
+ modified?: boolean;
36
+ jobData?: unknown;
37
+ stop?: boolean;
38
+ error?: Error;
39
+ };
40
+
41
+ export type PluginMetadata = {
42
+ name: string;
43
+ version: string;
44
+ author?: string;
45
+ description?: string;
46
+ dependencies?: string[]; // Other plugin names this plugin depends on
47
+ };
48
+
49
+ export type Plugin = {
50
+ metadata: PluginMetadata;
51
+ hooks: Partial<Record<PluginHook, PluginHookHandler>>;
52
+ onEnable?: () => void | Promise<void>;
53
+ onDisable?: () => void | Promise<void>;
54
+ };
55
+
56
+ export type PluginHookHandler = (
57
+ context: HookContext
58
+ ) => HookResult | Promise<HookResult> | undefined | Promise<HookResult | undefined>;
59
+
60
+ export type RegisteredPlugin = {
61
+ plugin: Plugin;
62
+ enabled: boolean;
63
+ registeredAt: Date;
64
+ priority: number; // Lower number = higher priority (executes first)
65
+ };
66
+
67
+ export type HookExecutionResult = {
68
+ data: unknown;
69
+ hook: PluginHook;
70
+ executionTime: number;
71
+ pluginsExecuted: number;
72
+ errors: Array<{ pluginName: string; error: Error }>;
73
+ context: HookContext;
74
+ modified: boolean;
75
+ stopped: boolean;
76
+ };
77
+
78
+ // Internal state
79
+ const plugins = new Map<string, RegisteredPlugin>();
80
+ const hookExecutionHistory: HookExecutionResult[] = [];
81
+ const MAX_HISTORY = 1000;
82
+
83
+ /**
84
+ * Helper: Validate plugin metadata
85
+ */
86
+ const validatePlugin = (plugin: Plugin): void => {
87
+ if (!plugin.metadata.name) {
88
+ throw ErrorFactory.createWorkerError('Plugin name is required');
89
+ }
90
+
91
+ if (!plugin.metadata.version) {
92
+ throw ErrorFactory.createWorkerError('Plugin version is required');
93
+ }
94
+
95
+ if (Object.keys(plugin.hooks).length === 0) {
96
+ throw ErrorFactory.createWorkerError('Plugin must implement at least one hook');
97
+ }
98
+ };
99
+
100
+ /**
101
+ * Helper: Check plugin dependencies
102
+ */
103
+ const checkDependencies = (plugin: Plugin): { satisfied: boolean; missing: string[] } => {
104
+ if (!plugin.metadata.dependencies || plugin.metadata.dependencies.length === 0) {
105
+ return { satisfied: true, missing: [] };
106
+ }
107
+
108
+ const missing: string[] = [];
109
+
110
+ for (const depName of plugin.metadata.dependencies) {
111
+ const dep = plugins.get(depName);
112
+ if (dep?.enabled !== true) {
113
+ missing.push(depName);
114
+ }
115
+ }
116
+
117
+ return {
118
+ satisfied: missing.length === 0,
119
+ missing,
120
+ };
121
+ };
122
+
123
+ /**
124
+ * Helper: Get enabled plugins for a hook, sorted by priority
125
+ */
126
+ const getEnabledPluginsForHook = (hook: PluginHook): RegisteredPlugin[] => {
127
+ const enabledPlugins: RegisteredPlugin[] = [];
128
+
129
+ for (const registered of plugins.values()) {
130
+ if (registered.enabled && registered.plugin.hooks[hook]) {
131
+ enabledPlugins.push(registered);
132
+ }
133
+ }
134
+
135
+ // Sort by priority (lower number = higher priority)
136
+ return enabledPlugins.sort((a, b) => a.priority - b.priority);
137
+ };
138
+
139
+ /**
140
+ * Helper: Store execution result
141
+ */
142
+ const storeExecutionResult = (result: HookExecutionResult): void => {
143
+ hookExecutionHistory.push(result);
144
+
145
+ // Keep only last MAX_HISTORY results
146
+ if (hookExecutionHistory.length > MAX_HISTORY) {
147
+ hookExecutionHistory.shift();
148
+ }
149
+ };
150
+
151
+ /**
152
+ * Plugin Manager - Sealed namespace
153
+ */
154
+ export const PluginManager = Object.freeze({
155
+ /**
156
+ * Register a plugin
157
+ */
158
+ async register(plugin: Plugin, priority = 100): Promise<void> {
159
+ validatePlugin(plugin);
160
+
161
+ const { name } = plugin.metadata;
162
+
163
+ if (plugins.has(name)) {
164
+ throw ErrorFactory.createWorkerError(`Plugin "${name}" is already registered`);
165
+ }
166
+
167
+ // Check dependencies
168
+ const depCheck = checkDependencies(plugin);
169
+ if (!depCheck.satisfied) {
170
+ throw ErrorFactory.createWorkerError(
171
+ `Plugin "${name}" has unsatisfied dependencies: ${depCheck.missing.join(', ')}`
172
+ );
173
+ }
174
+
175
+ const registered: RegisteredPlugin = {
176
+ plugin,
177
+ enabled: true,
178
+ registeredAt: new Date(),
179
+ priority,
180
+ };
181
+
182
+ plugins.set(name, registered);
183
+
184
+ // Call onEnable if provided
185
+ if (plugin.onEnable) {
186
+ try {
187
+ await plugin.onEnable();
188
+ } catch (error) {
189
+ Logger.error(`Plugin "${name}" onEnable failed`, error);
190
+ plugins.delete(name);
191
+ throw error;
192
+ }
193
+ }
194
+
195
+ Logger.info(`Plugin registered: ${name}@${plugin.metadata.version}`, {
196
+ hooks: Object.keys(plugin.hooks),
197
+ priority,
198
+ });
199
+ },
200
+
201
+ /**
202
+ * Unregister a plugin
203
+ */
204
+ async unregister(pluginName: string): Promise<void> {
205
+ const registered = plugins.get(pluginName);
206
+
207
+ if (!registered) {
208
+ throw ErrorFactory.createNotFoundError(`Plugin "${pluginName}" not found`);
209
+ }
210
+
211
+ // Check if other plugins depend on this one
212
+ const dependents: string[] = [];
213
+ for (const [name, reg] of plugins.entries()) {
214
+ if (name !== pluginName && reg.enabled) {
215
+ const deps = reg.plugin.metadata.dependencies ?? [];
216
+ if (deps.includes(pluginName)) {
217
+ dependents.push(name);
218
+ }
219
+ }
220
+ }
221
+
222
+ if (dependents.length > 0) {
223
+ throw ErrorFactory.createWorkerError(
224
+ `Cannot unregister plugin "${pluginName}": required by ${dependents.join(', ')}`
225
+ );
226
+ }
227
+
228
+ // Call onDisable if provided
229
+ if (registered.plugin.onDisable) {
230
+ try {
231
+ await registered.plugin.onDisable();
232
+ } catch (error) {
233
+ Logger.error(`Plugin "${pluginName}" onDisable failed`, error);
234
+ }
235
+ }
236
+
237
+ plugins.delete(pluginName);
238
+
239
+ Logger.info(`Plugin unregistered: ${pluginName}`);
240
+ },
241
+
242
+ /**
243
+ * Enable a plugin
244
+ */
245
+ async enable(pluginName: string): Promise<void> {
246
+ const registered = plugins.get(pluginName);
247
+
248
+ if (!registered) {
249
+ throw ErrorFactory.createNotFoundError(`Plugin "${pluginName}" not found`);
250
+ }
251
+
252
+ if (registered.enabled) {
253
+ Logger.warn(`Plugin "${pluginName}" is already enabled`);
254
+ return;
255
+ }
256
+
257
+ // Check dependencies
258
+ const depCheck = checkDependencies(registered.plugin);
259
+ if (!depCheck.satisfied) {
260
+ throw ErrorFactory.createWorkerError(
261
+ `Cannot enable plugin "${pluginName}": unsatisfied dependencies: ${depCheck.missing.join(', ')}`
262
+ );
263
+ }
264
+
265
+ registered.enabled = true;
266
+
267
+ // Call onEnable if provided
268
+ if (registered.plugin.onEnable) {
269
+ try {
270
+ await registered.plugin.onEnable();
271
+ } catch (error) {
272
+ Logger.error(`Plugin "${pluginName}" onEnable failed`, error);
273
+ registered.enabled = false;
274
+ throw error;
275
+ }
276
+ }
277
+
278
+ Logger.info(`Plugin enabled: ${pluginName}`);
279
+ },
280
+
281
+ /**
282
+ * Disable a plugin
283
+ */
284
+ async disable(pluginName: string): Promise<void> {
285
+ const registered = plugins.get(pluginName);
286
+
287
+ if (!registered) {
288
+ throw ErrorFactory.createNotFoundError(`Plugin "${pluginName}" not found`);
289
+ }
290
+
291
+ if (!registered.enabled) {
292
+ Logger.warn(`Plugin "${pluginName}" is already disabled`);
293
+ return;
294
+ }
295
+
296
+ // Check if other enabled plugins depend on this one
297
+ const dependents: string[] = [];
298
+ for (const [name, reg] of plugins.entries()) {
299
+ if (name !== pluginName && reg.enabled) {
300
+ const deps = reg.plugin.metadata.dependencies ?? [];
301
+ if (deps.includes(pluginName)) {
302
+ dependents.push(name);
303
+ }
304
+ }
305
+ }
306
+
307
+ if (dependents.length > 0) {
308
+ throw ErrorFactory.createWorkerError(
309
+ `Cannot disable plugin "${pluginName}": required by enabled plugins: ${dependents.join(', ')}`
310
+ );
311
+ }
312
+
313
+ registered.enabled = false;
314
+
315
+ // Call onDisable if provided
316
+ if (registered.plugin.onDisable) {
317
+ try {
318
+ await registered.plugin.onDisable();
319
+ } catch (error) {
320
+ Logger.error(`Plugin "${pluginName}" onDisable failed`, error);
321
+ }
322
+ }
323
+
324
+ Logger.info(`Plugin disabled: ${pluginName}`);
325
+ },
326
+
327
+ /**
328
+ * Execute hooks for a lifecycle event
329
+ */
330
+ async executeHook(hook: PluginHook, context: HookContext): Promise<HookExecutionResult> {
331
+ const startTime = Date.now();
332
+ const enabledPlugins = getEnabledPluginsForHook(hook);
333
+
334
+ const result: HookExecutionResult = {
335
+ data: context.jobData,
336
+ hook,
337
+ executionTime: 0,
338
+ pluginsExecuted: 0,
339
+ errors: [],
340
+ context,
341
+ modified: false,
342
+ stopped: false,
343
+ };
344
+
345
+ const currentContext = { ...context };
346
+
347
+ let stopped = false;
348
+
349
+ const executeHandler = async (registered: RegisteredPlugin): Promise<void> => {
350
+ if (stopped) return;
351
+
352
+ const { plugin } = registered;
353
+ const handler = plugin.hooks[hook];
354
+ if (!handler) return;
355
+
356
+ try {
357
+ const hookResult = await handler(currentContext);
358
+ result.pluginsExecuted++;
359
+
360
+ if (hookResult !== undefined) {
361
+ if (hookResult.modified === true) {
362
+ result.modified = true;
363
+ }
364
+
365
+ if (hookResult.jobData !== undefined) {
366
+ currentContext.jobData = hookResult.jobData;
367
+ result.data = hookResult.jobData;
368
+ }
369
+
370
+ if (hookResult.stop === true) {
371
+ result.stopped = true;
372
+ stopped = true;
373
+ Logger.warn(`Hook execution stopped by plugin: ${plugin.metadata.name}`, { hook });
374
+ }
375
+
376
+ if (hookResult.error instanceof Error) {
377
+ result.errors.push({
378
+ pluginName: plugin.metadata.name,
379
+ error: hookResult.error,
380
+ });
381
+ }
382
+ }
383
+ } catch (error) {
384
+ Logger.error(`Plugin "${plugin.metadata.name}" hook "${hook}" failed`, error);
385
+ result.errors.push({
386
+ pluginName: plugin.metadata.name,
387
+ error: error as Error,
388
+ });
389
+
390
+ if (hook === 'beforeStart' || hook === 'beforeStop') {
391
+ result.stopped = true;
392
+ stopped = true;
393
+ }
394
+ }
395
+ };
396
+
397
+ let chain = Promise.resolve();
398
+ for (const registered of enabledPlugins) {
399
+ chain = chain.then(async () => executeHandler(registered));
400
+ }
401
+
402
+ await chain;
403
+
404
+ result.executionTime = Date.now() - startTime;
405
+ storeExecutionResult(result);
406
+
407
+ Logger.debug(`Hook executed: ${hook}`, {
408
+ pluginsExecuted: result.pluginsExecuted,
409
+ executionTime: result.executionTime,
410
+ errors: result.errors.length,
411
+ });
412
+
413
+ return result;
414
+ },
415
+
416
+ /**
417
+ * Get registered plugins
418
+ */
419
+ getPlugins(): ReadonlyArray<RegisteredPlugin & { name: string }> {
420
+ return Array.from(plugins.entries()).map(([name, registered]) => ({
421
+ name,
422
+ ...registered,
423
+ }));
424
+ },
425
+
426
+ /**
427
+ * Get plugin by name
428
+ */
429
+ getPlugin(pluginName: string): (RegisteredPlugin & { name: string }) | null {
430
+ const registered = plugins.get(pluginName);
431
+ if (!registered) return null;
432
+
433
+ return {
434
+ name: pluginName,
435
+ ...registered,
436
+ };
437
+ },
438
+
439
+ /**
440
+ * Check if plugin is registered
441
+ */
442
+ isRegistered(pluginName: string): boolean {
443
+ return plugins.has(pluginName);
444
+ },
445
+
446
+ /**
447
+ * Check if plugin is enabled
448
+ */
449
+ isEnabled(pluginName: string): boolean {
450
+ const registered = plugins.get(pluginName);
451
+ return registered ? registered.enabled : false;
452
+ },
453
+
454
+ /**
455
+ * Get hook execution history
456
+ */
457
+ getExecutionHistory(hook?: PluginHook, limit = 100): ReadonlyArray<HookExecutionResult> {
458
+ let history = hookExecutionHistory;
459
+
460
+ if (hook) {
461
+ history = history.filter((result) => result.hook === hook);
462
+ }
463
+
464
+ return history.slice(-limit).map((result) => ({ ...result }));
465
+ },
466
+
467
+ /**
468
+ * Get plugin statistics
469
+ */
470
+ getStatistics(): {
471
+ totalPlugins: number;
472
+ enabledPlugins: number;
473
+ disabledPlugins: number;
474
+ totalHookExecutions: number;
475
+ hookExecutionsByType: Record<PluginHook, number>;
476
+ averageExecutionTime: number;
477
+ totalErrors: number;
478
+ } {
479
+ const stats = {
480
+ totalPlugins: plugins.size,
481
+ enabledPlugins: 0,
482
+ disabledPlugins: 0,
483
+ totalHookExecutions: hookExecutionHistory.length,
484
+ hookExecutionsByType: {} as Record<PluginHook, number>,
485
+ averageExecutionTime: 0,
486
+ totalErrors: 0,
487
+ };
488
+
489
+ for (const registered of plugins.values()) {
490
+ if (registered.enabled) {
491
+ stats.enabledPlugins++;
492
+ } else {
493
+ stats.disabledPlugins++;
494
+ }
495
+ }
496
+
497
+ let totalExecutionTime = 0;
498
+
499
+ for (const result of hookExecutionHistory) {
500
+ const hookType = result.hook;
501
+ stats.hookExecutionsByType[hookType] = (stats.hookExecutionsByType[hookType] || 0) + 1;
502
+ totalExecutionTime += result.executionTime;
503
+ stats.totalErrors += result.errors.length;
504
+ }
505
+
506
+ if (hookExecutionHistory.length > 0) {
507
+ stats.averageExecutionTime = totalExecutionTime / hookExecutionHistory.length;
508
+ }
509
+
510
+ return stats;
511
+ },
512
+
513
+ /**
514
+ * Clear execution history
515
+ */
516
+ clearHistory(): void {
517
+ hookExecutionHistory.length = 0;
518
+ Logger.info('Plugin execution history cleared');
519
+ },
520
+
521
+ /**
522
+ * Shutdown and disable all plugins
523
+ */
524
+ async shutdown(): Promise<void> {
525
+ Logger.info('PluginManager shutting down...');
526
+
527
+ // Disable all plugins in reverse order of priority
528
+ const sortedPlugins = Array.from(plugins.entries()).sort(
529
+ ([, a], [, b]) => b.priority - a.priority
530
+ );
531
+
532
+ const disableTasks = sortedPlugins.map(async ([name, registered]) => {
533
+ if (!registered.enabled || !registered.plugin.onDisable) return;
534
+ try {
535
+ await registered.plugin.onDisable();
536
+ Logger.debug(`Plugin disabled: ${name}`);
537
+ } catch (error) {
538
+ Logger.error(`Plugin "${name}" onDisable failed during shutdown`, error);
539
+ }
540
+ });
541
+
542
+ await Promise.allSettled(disableTasks);
543
+
544
+ plugins.clear();
545
+ hookExecutionHistory.length = 0;
546
+
547
+ Logger.info('PluginManager shutdown complete');
548
+ },
549
+ });
550
+
551
+ // Graceful shutdown handled by WorkerShutdown