/**
 * Redirect graph utilities — pure functions for chain/loop detection,
 * loop-prevention on write, path normalization, and stale detection.
 *
 * Lives in cms-core (not plugin-redirects) because the API layer needs it and
 * the dependency direction forbids core importing a plugin. The plugin's
 * request-time middleware can stay independent.
 */
export interface RedirectRecord {
    id: string;
    from: string;
    to: string;
    enabled?: boolean;
    statusCode?: number;
}
/**
 * Normalize a path for matching: drop query/hash, ensure a single leading
 * slash, strip a trailing slash (except root), and lowercase. External
 * absolute URLs are returned lowercased without a forced leading slash.
 */
export declare function normalizeRedirectPath(input: string): string;
/** True when a redirect points to an external (absolute) destination. */
export declare function isExternal(to: string): boolean;
interface Edge {
    to: string;
    toNorm: string;
    id: string;
}
/** Build a normalized from -> edge map of enabled rules. */
export declare function buildEdgeMap(rules: RedirectRecord[]): Map<string, Edge>;
export interface ChainResolution {
    /** Normalized path sequence, starting at `start`. */
    hops: string[];
    /** Final destination (normalized) the chain settles on, or the loop entry. */
    terminal: string;
    /** True when a cycle was detected. */
    loop: boolean;
}
/**
 * Follow the redirect map from `start` until a path with no further mapping
 * (terminal) or a cycle is reached. Bounded by `maxHops`.
 */
export declare function resolveChain(map: Map<string, Edge>, start: string, maxHops?: number): ChainResolution;
export interface DetectedChain {
    id: string;
    kind: 'chain' | 'loop';
    /** Full hop sequence (normalized paths). */
    hops: string[];
    /** Suggested single-hop source. */
    suggestedFrom: string;
    /** Suggested single-hop destination (final terminal, or loop break). */
    suggestedTo: string;
}
/**
 * Detect multi-hop chains (3+ nodes => 2+ redirects) and loops across the
 * rule set. Returns one entry per chain/loop keyed by a stable id derived from
 * the starting source.
 */
export declare function detectChainsAndLoops(rules: RedirectRecord[]): DetectedChain[];
/**
 * Would adding (or updating to) `from -> to` create a loop or self-redirect?
 * Simulates the new edge over the existing rule set.
 */
export declare function wouldCreateLoop(rules: RedirectRecord[], from: string, to: string, ignoreId?: string): boolean;
/** A redirect is stale when it hasn't been hit in 60+ days. */
export declare function isRedirectStale(lastHitAt: Date | string | null | undefined, now?: Date): boolean;
export {};
//# sourceMappingURL=graph.d.ts.map