import { type CommandParser } from '../../client/parser';
import type { RequestRouter, RoutedCommand } from './dispatch';
type ClusterSlots = Parameters<RequestRouter>[0];
/**
 * Cluster-wide SCAN and SCAN-shaped commands (server `request_policy: special`
 * + `response_policy: special`, e.g. SCAN, BLESS SCAN). Their cursors are
 * per-node state, so the cluster iteration walks the masters one at a time
 * behind a *virtual* cursor:
 *
 *  - cursor `0` starts a chain on the first master.
 *  - Each reply's server cursor is swapped for a client-minted token that maps
 *    back to (serving node, real cursor, masters already exhausted). The
 *    caller keeps its usual `cursor !== "0"` loop; the token is opaque.
 *  - When a node's cursor returns 0 the chain advances to the next unvisited
 *    master (fresh cursor 0); when every master is exhausted the caller
 *    finally sees "0".
 *
 * The visited set is tracked by node address, so a topology change mid-scan
 * neither rescans a surviving node nor gets stuck on a departed one. The usual
 * SCAN guarantees apply per node; keys migrating between nodes mid-iteration
 * may be missed or duplicated — same caveat as every cluster-wide scan.
 *
 * Tokens are minted from one counter shared by every scan-chained command
 * (see `ScanCursorEntry.label` in `cluster-slots.ts`), so a token bound by one
 * command is rejected — not silently reused — if handed to another.
 */
export declare const routeScan: RequestRouter;
/**
 * Post-reply hook for scan-chained commands (invoked from
 * `_executeWithPolicies` after the reducer, like `finalizeFtCursor`):
 * advances the chain state and swaps the server cursor in the reply for the
 * chain's virtual token. No-op for any other command or a non-single-target
 * plan. Returns the (possibly rewritten) reply.
 */
export declare function finalizeScanCursor(slots: ClusterSlots, parser: CommandParser, plan: ReadonlyArray<RoutedCommand>, reply: unknown): unknown;
export {};
//# sourceMappingURL=scan-cursor.d.ts.map