/**
 * Prefix-based actor search.
 *
 * Sibling to `actor_lookup_queries.ts` — that resolves a batch of ids to
 * labels; this resolves a partial name to candidate actors. Same row
 * shape (`ActorLookupRow`) so the labels arc on the consumer side stays
 * uniform.
 *
 * Case-insensitive LIKE-prefix on `actor.name` backed by the
 * `idx_actor_name_lower` functional index. LIKE wildcards (`%`, `_`,
 * `\`) in the query string are escaped at the JS layer so the
 * prefix-only contract is enforceable — an unescaped `%xyz` would
 * widen the surface to full-LIKE and defeat the per-call cap as a
 * binding bound.
 *
 * ## Auth filtering — `scope_ids`
 *
 * When `scope_ids` is non-empty, the result is filtered to actors
 * holding an **active** role_grant on one of the supplied scopes. Active
 * means `revoked_at IS NULL AND (expires_at IS NULL OR expires_at > NOW())`.
 * Stale (revoked / expired) role_grants do **not** confer membership for
 * search-visibility purposes — otherwise a removed student would
 * remain visible to teachers indefinitely.
 *
 * The `DISTINCT` on `actor.id` collapses the case where an actor holds
 * multiple matching role_grants in the supplied scope set into one row.
 *
 * When `scope_ids` is omitted (admin-only global path; the handler
 * gates), no role_grant join — every actor with a matching prefix is
 * returned.
 *
 * ## Info-leak posture (see `actor_search_action_specs.ts` §audit)
 *
 * - Row shape **omits** `account_id` — the join is control-plane, not
 *   wire-visible. Identical to `actor_lookup_queries.ts`.
 * - Hard-deleted actors (cascade-orphaned via `actor.account_id` FK)
 *   drop out silently.
 * - No `created_at` / `updated_at` projected (timing-oracle avoidance).
 * - Scope-membership uses ANY on the supplied `scope_ids` array but
 *   never surfaces "which scope matched" — the result row carries only
 *   the actor's wire shape. An attacker passing a random scope_id
 *   learns at most "this scope has at least one member matching X"
 *   if a match exists, indistinguishable from a no-match search; the
 *   caller-passes-scope_ids design (handler trusts the array as a
 *   filter, not as authority) means the attacker had to obtain the
 *   scope_id from somewhere else first.
 *
 * Caller bounds `limit` (the action-spec layer enforces
 * `ACTOR_SEARCH_LIMIT_MAX`); SQL clamps to that cap on the call site
 * before reaching this query.
 *
 * @module
 */
import type { Uuid } from '@fuzdev/fuz_util/id.js';
import type { QueryDeps } from '../db/query_deps.js';
import type { ActorLookupRow } from './actor_lookup_queries.js';
/** Inputs for `query_actor_search`. */
export interface ActorSearchQueryInput {
    /** Case-insensitive prefix string. Must be non-empty (action layer enforces `min(1)`). */
    query: string;
    /**
     * When non-empty, restrict to actors holding an active role_grant on one
     * of these scope ids. When empty / omitted, no scope filter is applied —
     * the handler is responsible for the admin gate.
     */
    scope_ids?: ReadonlyArray<Uuid>;
    /** Maximum rows to return. The handler clamps to `ACTOR_SEARCH_LIMIT_MAX`. */
    limit: number;
}
/**
 * Search actors by case-insensitive prefix on `actor.name`, optionally
 * filtered to those holding an active role_grant on one of `scope_ids`.
 */
export declare const query_actor_search: (deps: QueryDeps, input: ActorSearchQueryInput) => Promise<Array<ActorLookupRow>>;
//# sourceMappingURL=actor_search_queries.d.ts.map