tasq/node_modules/@claude-flow/shared/dist/hooks/registry.js

277 lines
7.4 KiB
JavaScript

/**
* V3 Hooks System - Hook Registry
*
* Central registry for managing hook definitions and lifecycle.
* Provides registration, unregistration, and discovery of hooks.
*
* @module v3/shared/hooks/registry
*/
import { HookPriority, } from './types.js';
/**
* Hook registry implementation
*/
export class HookRegistry {
hooks = new Map();
hooksById = new Map();
hookIdCounter = 0;
// Statistics tracking
stats = {
executions: 0,
failures: 0,
totalExecutionTime: 0,
};
/**
* Register a new hook
*
* @param event - Hook event type
* @param handler - Hook handler function
* @param priority - Hook priority (default: Normal)
* @param options - Additional hook options
* @returns Hook ID for later unregistration
*/
register(event, handler, priority = HookPriority.Normal, options = {}) {
// Generate unique hook ID
const id = `hook_${++this.hookIdCounter}_${Date.now()}`;
// Create hook definition
const definition = {
id,
event,
handler,
priority,
name: options.name,
enabled: options.enabled ?? true,
timeout: options.timeout,
metadata: options.metadata,
};
// Add to event-specific list
let eventHooks = this.hooks.get(event);
if (!eventHooks) {
eventHooks = [];
this.hooks.set(event, eventHooks);
}
eventHooks.push(definition);
// Sort by priority (highest first)
eventHooks.sort((a, b) => b.priority - a.priority);
// Add to ID map
this.hooksById.set(id, definition);
return id;
}
/**
* Unregister a hook by ID
*
* @param hookId - Hook ID to unregister
* @returns Whether hook was found and removed
*/
unregister(hookId) {
const definition = this.hooksById.get(hookId);
if (!definition) {
return false;
}
// Remove from event-specific list
const eventHooks = this.hooks.get(definition.event);
if (eventHooks) {
const index = eventHooks.findIndex(h => h.id === hookId);
if (index !== -1) {
eventHooks.splice(index, 1);
}
// Clean up empty arrays
if (eventHooks.length === 0) {
this.hooks.delete(definition.event);
}
}
// Remove from ID map
this.hooksById.delete(hookId);
return true;
}
/**
* Unregister all hooks for an event
*
* @param event - Event type to clear hooks for
* @returns Number of hooks removed
*/
unregisterAll(event) {
if (event) {
const eventHooks = this.hooks.get(event) || [];
const count = eventHooks.length;
// Remove from ID map
for (const hook of eventHooks) {
this.hooksById.delete(hook.id);
}
// Clear event hooks
this.hooks.delete(event);
return count;
}
else {
// Clear all hooks
const count = this.hooksById.size;
this.hooks.clear();
this.hooksById.clear();
this.hookIdCounter = 0;
return count;
}
}
/**
* Get all hooks for a specific event (sorted by priority)
*
* @param event - Event type
* @param includeDisabled - Whether to include disabled hooks
* @returns Array of hook definitions
*/
getHandlers(event, includeDisabled = false) {
const eventHooks = this.hooks.get(event) || [];
if (includeDisabled) {
return [...eventHooks];
}
return eventHooks.filter(h => h.enabled);
}
/**
* Get a hook by ID
*
* @param hookId - Hook ID
* @returns Hook definition or undefined
*/
getHook(hookId) {
return this.hooksById.get(hookId);
}
/**
* Enable a hook
*
* @param hookId - Hook ID
* @returns Whether hook was found and enabled
*/
enable(hookId) {
const hook = this.hooksById.get(hookId);
if (hook) {
hook.enabled = true;
return true;
}
return false;
}
/**
* Disable a hook
*
* @param hookId - Hook ID
* @returns Whether hook was found and disabled
*/
disable(hookId) {
const hook = this.hooksById.get(hookId);
if (hook) {
hook.enabled = false;
return true;
}
return false;
}
/**
* List all registered hooks
*
* @param filter - Optional filter options
* @returns Array of hook definitions
*/
listHooks(filter) {
let hooks;
if (filter?.event) {
hooks = this.hooks.get(filter.event) || [];
}
else {
hooks = Array.from(this.hooksById.values());
}
// Apply filters
if (filter?.enabled !== undefined) {
hooks = hooks.filter(h => h.enabled === filter.enabled);
}
if (filter?.minPriority !== undefined) {
const minPriority = filter.minPriority;
hooks = hooks.filter(h => h.priority >= minPriority);
}
return hooks;
}
/**
* Get all event types with registered hooks
*
* @returns Array of event types
*/
getEventTypes() {
return Array.from(this.hooks.keys());
}
/**
* Get count of hooks for an event
*
* @param event - Event type (optional)
* @returns Hook count
*/
count(event) {
if (event) {
return this.hooks.get(event)?.length || 0;
}
return this.hooksById.size;
}
/**
* Record hook execution statistics
*
* @param success - Whether execution succeeded
* @param executionTime - Execution time in ms
*/
recordExecution(success, executionTime) {
this.stats.executions++;
this.stats.totalExecutionTime += executionTime;
if (!success) {
this.stats.failures++;
}
}
/**
* Get hook statistics
*
* @returns Hook statistics
*/
getStats() {
const byEvent = {};
for (const [event, hooks] of this.hooks) {
byEvent[event] = hooks.filter(h => h.enabled).length;
}
return {
totalHooks: this.hooksById.size,
byEvent,
totalExecutions: this.stats.executions,
totalFailures: this.stats.failures,
avgExecutionTime: this.stats.executions > 0
? this.stats.totalExecutionTime / this.stats.executions
: 0,
totalExecutionTime: this.stats.totalExecutionTime,
};
}
/**
* Reset statistics
*/
resetStats() {
this.stats = {
executions: 0,
failures: 0,
totalExecutionTime: 0,
};
}
/**
* Check if a hook exists
*
* @param hookId - Hook ID
* @returns Whether hook exists
*/
has(hookId) {
return this.hooksById.has(hookId);
}
/**
* Clear all hooks and reset state
*/
clear() {
this.hooks.clear();
this.hooksById.clear();
this.hookIdCounter = 0;
this.resetStats();
}
}
/**
* Create a new hook registry
*/
export function createHookRegistry() {
return new HookRegistry();
}
//# sourceMappingURL=registry.js.map