import type { HookContext } from '@feathersjs/feathers' import type { PredicateFn } from '../../types.js' import { isPromise } from '../../common/index.js' /** * Returns a predicate that is `true` only when **all** given predicates are `true` (logical AND). * Supports both sync and async predicates. Short-circuits on the first `false` result. * Undefined predicates in the list are skipped. * * @example * ```ts * import { iff, and, isProvider, isMulti } from 'feathers-utils/predicates' * * app.service('users').hooks({ * before: { all: [iff(and(isProvider('external'), isMulti), checkMulti())] } * }) * ``` * * @see https://utils.feathersjs.com/predicates/and.html */ export const and = ( ...predicates: (PredicateFn | undefined)[] ): PredicateFn => { const filtered = predicates.filter( (p): p is PredicateFn => p !== undefined, ) return (context: H): boolean | Promise => { // The identity element of logical AND is `true` (an empty AND is true). if (!filtered.length) { return true } const promises: Promise[] = [] for (const predicate of filtered) { const result = predicate(context) if (isPromise(result)) { promises.push(result) } else if (!result) { // any falsy sync result short-circuits to false return false } } if (!promises.length) { return true } return Promise.all(promises).then((results) => results.every((result) => !!result), ) } } // Alias for 'every' export const every = and