feat(shared): add safeCreateHook utility for error-safe hook creation
This commit is contained in:
@@ -41,3 +41,4 @@ export * from "./tmux"
|
||||
export * from "./model-suggestion-retry"
|
||||
export * from "./opencode-server-auth"
|
||||
export * from "./port-utils"
|
||||
export * from "./safe-create-hook"
|
||||
|
||||
73
src/shared/safe-create-hook.test.ts
Normal file
73
src/shared/safe-create-hook.test.ts
Normal file
@@ -0,0 +1,73 @@
|
||||
import { describe, test, expect, spyOn, afterEach } from "bun:test"
|
||||
import * as shared from "./logger"
|
||||
import { safeCreateHook } from "./safe-create-hook"
|
||||
|
||||
afterEach(() => {
|
||||
;(shared.log as any)?.mockRestore?.()
|
||||
})
|
||||
|
||||
describe("safeCreateHook", () => {
|
||||
test("returns hook object when factory succeeds", () => {
|
||||
//#given
|
||||
const hook = { handler: () => {} }
|
||||
const factory = () => hook
|
||||
|
||||
//#when
|
||||
const result = safeCreateHook("test-hook", factory)
|
||||
|
||||
//#then
|
||||
expect(result).toBe(hook)
|
||||
})
|
||||
|
||||
test("returns null when factory throws", () => {
|
||||
//#given
|
||||
spyOn(shared, "log").mockImplementation(() => {})
|
||||
const factory = () => {
|
||||
throw new Error("boom")
|
||||
}
|
||||
|
||||
//#when
|
||||
const result = safeCreateHook("test-hook", factory)
|
||||
|
||||
//#then
|
||||
expect(result).toBeNull()
|
||||
})
|
||||
|
||||
test("logs error when factory throws", () => {
|
||||
//#given
|
||||
const logSpy = spyOn(shared, "log").mockImplementation(() => {})
|
||||
const factory = () => {
|
||||
throw new Error("boom")
|
||||
}
|
||||
|
||||
//#when
|
||||
safeCreateHook("my-hook", factory)
|
||||
|
||||
//#then
|
||||
expect(logSpy).toHaveBeenCalled()
|
||||
const callArgs = logSpy.mock.calls[0]
|
||||
expect(callArgs[0]).toContain("my-hook")
|
||||
expect(callArgs[0]).toContain("Hook creation failed")
|
||||
})
|
||||
|
||||
test("propagates error when enabled is false", () => {
|
||||
//#given
|
||||
const factory = () => {
|
||||
throw new Error("boom")
|
||||
}
|
||||
|
||||
//#when + #then
|
||||
expect(() => safeCreateHook("test-hook", factory, { enabled: false })).toThrow("boom")
|
||||
})
|
||||
|
||||
test("returns null for factory returning undefined", () => {
|
||||
//#given
|
||||
const factory = () => undefined as any
|
||||
|
||||
//#when
|
||||
const result = safeCreateHook("test-hook", factory)
|
||||
|
||||
//#then
|
||||
expect(result).toBeNull()
|
||||
})
|
||||
})
|
||||
24
src/shared/safe-create-hook.ts
Normal file
24
src/shared/safe-create-hook.ts
Normal file
@@ -0,0 +1,24 @@
|
||||
import { log } from "./logger"
|
||||
|
||||
interface SafeCreateHookOptions {
|
||||
enabled?: boolean
|
||||
}
|
||||
|
||||
export function safeCreateHook<T>(
|
||||
name: string,
|
||||
factory: () => T,
|
||||
options?: SafeCreateHookOptions,
|
||||
): T | null {
|
||||
const enabled = options?.enabled ?? true
|
||||
|
||||
if (!enabled) {
|
||||
return factory() ?? null
|
||||
}
|
||||
|
||||
try {
|
||||
return factory() ?? null
|
||||
} catch (error) {
|
||||
log(`[safe-create-hook] Hook creation failed: ${name}`, { error })
|
||||
return null
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user