import { isDefined } from './isDefined.js'; type AsyncDidEndHook = (...args: TArgs) => Promise; type SyncDidEndHook = (...args: TArgs) => void; export async function invokeDidStartHook( targets: T[], hook: (t: T) => Promise | undefined | void>, ): Promise> { const didEndHooks = ( await Promise.all(targets.map((target) => hook(target))) ).filter(isDefined); didEndHooks.reverse(); return async (...args: TEndHookArgs) => { for (const didEndHook of didEndHooks) { didEndHook(...args); } }; } // Almost all hooks are async, but as a special case, willResolveField is sync // due to performance concerns. export function invokeSyncDidStartHook( targets: T[], hook: (t: T) => SyncDidEndHook | undefined | void, ): SyncDidEndHook { const didEndHooks: SyncDidEndHook[] = targets .map((target) => hook(target)) .filter(isDefined); didEndHooks.reverse(); return (...args: TEndHookArgs) => { for (const didEndHook of didEndHooks) { didEndHook(...args); } }; } export async function invokeHooksUntilDefinedAndNonNull( targets: T[], hook: (t: T) => Promise, ): Promise { for (const target of targets) { const value = await hook(target); if (value != null) { return value; } } return null; }