import { createPatchedFunctionProxy, type FunctionProxyState, type InsteadHookNode, type PatchedFunctionProxyState, patchedFunctionProxyHandler, patchedFunctionProxyStates, unproxy, } from '../_internal' import type { AbstractNewable, Callable, FiniteDomain, InsteadHook, UnknownFunction, } from '../types' const insteadHookProxyHandler = { ...patchedFunctionProxyHandler, apply( hookNode: InsteadHookNode, receiver: ThisParameterType, args: Parameters, ) { const { next } = hookNode return Reflect.apply(hookNode.hook, receiver, [ args, next === undefined ? hookNode.target : next.proxy, ]) }, construct>( hookNode: InsteadHookNode, args: ConstructorParameters, ctor: AbstractNewable, ) { const { next } = hookNode return Reflect.construct( hookNode.hook, [args, next === undefined ? hookNode.target : next.proxy], ctor, ) }, } as const satisfies Required> function unpatchInstead( state: PatchedFunctionProxyState, hookNode: InsteadHookNode, ) { if (hookNode.unpatched) return hookNode.unpatched = true const { prev, next } = hookNode if (prev === undefined) { state.instead = next if (next === undefined) { if (state.before === undefined && state.after === undefined) unproxy(state) return } } else { prev.next = next hookNode.prev = undefined if (next === undefined) return } next.prev = prev hookNode.next = undefined } export function instead< Parent extends Record, Key extends PropertyKey, >( parent: Parent, key: FiniteDomain, hook: InsteadHook, ): () => void export function instead( parent: Record, key: FiniteDomain, hook: InsteadHook, ) { const target = parent[key] let state = patchedFunctionProxyStates.get(target) // biome-ignore lint/complexity/useArrowFunction: We need a function that has a constructor const hookNode = function () {} // The handler makes the proxy behave as target. hookNode.proxy = new Proxy(hookNode, insteadHookProxyHandler) as Value hookNode.target = target hookNode.unpatched = false hookNode.hook = hook hookNode.prev = undefined if (state !== undefined && state.parent === parent && state.key === key) { const head = state.instead hookNode.next = head if (head !== undefined) head.prev = hookNode state.instead = hookNode } else { hookNode.next = undefined state = createPatchedFunctionProxy( target, parent, key, undefined, hookNode, undefined, ) } return unpatchInstead.bind(undefined, state, hookNode) }