UNPKG

@stackmemoryai/stackmemory

Version:

Project-scoped memory for AI coding tools. Durable context across sessions with MCP integration, frames, smart retrieval, Claude Code skills, and automatic hooks.

120 lines (119 loc) 4.01 kB
import { fileURLToPath as __fileURLToPath } from 'url'; import { dirname as __pathDirname } from 'path'; const __filename = __fileURLToPath(import.meta.url); const __dirname = __pathDirname(__filename); import { logger } from "../monitoring/logger.js"; class FrameLifecycleHooksRegistry { closeHooks = []; createHooks = []; /** * Register a hook to be called when a frame is closed * @param name - Unique name for the hook (for logging/debugging) * @param handler - Async function to call when frame closes * @param priority - Higher priority hooks run first (default: 0) */ onFrameClosed(name, handler, priority = 0) { const hook = { name, handler, priority }; this.closeHooks.push(hook); this.closeHooks.sort((a, b) => b.priority - a.priority); logger.debug("Registered frame close hook", { name, priority }); return () => { this.closeHooks = this.closeHooks.filter((h) => h !== hook); logger.debug("Unregistered frame close hook", { name }); }; } /** * Register a hook to be called when a frame is created * @param name - Unique name for the hook (for logging/debugging) * @param handler - Async function to call when frame is created * @param priority - Higher priority hooks run first (default: 0) */ onFrameCreated(name, handler, priority = 0) { const hook = { name, handler, priority }; this.createHooks.push(hook); this.createHooks.sort((a, b) => b.priority - a.priority); logger.debug("Registered frame create hook", { name, priority }); return () => { this.createHooks = this.createHooks.filter((h) => h !== hook); logger.debug("Unregistered frame create hook", { name }); }; } /** * Trigger all close hooks (called by FrameManager) * Hooks are fire-and-forget - errors don't affect frame closure */ async triggerClose(data) { if (this.closeHooks.length === 0) return; const results = await Promise.allSettled( this.closeHooks.map(async (hook) => { try { await hook.handler(data); } catch (error) { logger.warn(`Frame close hook "${hook.name}" failed`, { error: error instanceof Error ? error.message : String(error), frameId: data.frame.frame_id, frameName: data.frame.name }); } }) ); const failed = results.filter((r) => r.status === "rejected").length; if (failed > 0) { logger.debug("Some frame close hooks failed", { total: this.closeHooks.length, failed, frameId: data.frame.frame_id }); } } /** * Trigger all create hooks (called by FrameManager) * Hooks are fire-and-forget - errors don't affect frame creation */ async triggerCreate(frame) { if (this.createHooks.length === 0) return; const results = await Promise.allSettled( this.createHooks.map(async (hook) => { try { await hook.handler(frame); } catch (error) { logger.warn(`Frame create hook "${hook.name}" failed`, { error: error instanceof Error ? error.message : String(error), frameId: frame.frame_id, frameName: frame.name }); } }) ); const failed = results.filter((r) => r.status === "rejected").length; if (failed > 0) { logger.debug("Some frame create hooks failed", { total: this.createHooks.length, failed, frameId: frame.frame_id }); } } /** * Get count of registered hooks (useful for testing) */ getHookCounts() { return { close: this.closeHooks.length, create: this.createHooks.length }; } /** * Clear all hooks (useful for testing) */ clearAll() { this.closeHooks = []; this.createHooks = []; logger.debug("Cleared all frame lifecycle hooks"); } } const frameLifecycleHooks = new FrameLifecycleHooksRegistry(); export { frameLifecycleHooks }; //# sourceMappingURL=frame-lifecycle-hooks.js.map