/**
 * Per-process schema cache for the graph-mcp proxy.
 *
 * Caches { labels, relationshipTypes } from the connected Neo4j instance so
 * the cypher validator can look up tokens without a round-trip per call.
 *
 * Refresh triggers:
 *   - boot           — async refresh kicked off by start(); validator fails
 *                      OPEN until the first refresh resolves.
 *   - interval       — every 60s (configurable) while the process is alive.
 *   - stale-miss     — an unknown token with a near match (edit distance ≤ 2)
 *                      triggers a debounced refresh, catching fresh schema
 *                      additions without requiring operators to wait out the
 *                      interval.
 *
 * Failure posture:
 *   - A refresh that throws preserves the last good snapshot and leaves
 *     ready() as it was. `[schema-cache] refresh failure ...` is logged
 *     loudly so operators see persistent Neo4j unreachability.
 *   - A boot refresh that fails leaves ready()=false and the snapshot empty.
 *     The validator's own fail-open branch handles this (empty snapshot →
 *     ok=true, no rejections), so the graph-mcp proxy keeps working against
 *     a still-reachable Neo4j instance that's just slow to respond to the
 *     initial `db.labels()` call.
 */
import type { SchemaSnapshot, UnknownToken } from "./cypher-validate.js";
export interface SchemaFetcher {
    labels(): Promise<string[]>;
    relationshipTypes(): Promise<string[]>;
}
export interface SchemaCacheOptions {
    /** Interval between automatic refreshes. 0 disables the timer (tests). */
    refreshIntervalMs?: number;
    /** Minimum ms between stale-miss-triggered refreshes. Default 5000. */
    staleMissDebounceMs?: number;
    /** Emit a log line. Default: stderr. Tests inject to capture. */
    emit?: (line: string) => void;
}
export declare class SchemaCache {
    private readonly fetcher;
    private readonly options;
    private _labels;
    private _rels;
    private _ready;
    private _timer;
    private _refreshInFlight;
    private _lastStaleMissAt;
    constructor(fetcher: SchemaFetcher, options?: SchemaCacheOptions);
    snapshot(): SchemaSnapshot;
    ready(): boolean;
    start(): Promise<void>;
    stop(): void;
    /**
     * Refresh the cache. Single-flight: concurrent calls await the same in-flight
     * promise rather than triggering overlapping Neo4j reads. Returns true on
     * successful update, false on failure (last good snapshot preserved).
     */
    refresh(reason: string): Promise<boolean>;
    /**
     * If the validator returned unknown tokens whose nearest known token is
     * within STALE_MISS_DISTANCE_CEILING edits, trigger an out-of-band refresh
     * (debounced). The heuristic: a near-miss is likely a fresh schema
     * change (renamed / newly added label/edge), where a far-miss is almost
     * certainly a typo and a refresh would not change the outcome. Returns
     * true iff a refresh was actually run this call.
     */
    maybeRebuildOnStaleMiss(unknown: UnknownToken[]): Promise<boolean>;
    private emit;
}
export declare function getSharedDriver(uri: string, user: string, password: string): Promise<unknown>;
/**
 * Build a SchemaFetcher over the shared neo4j-driver Driver. Each call
 * opens a fresh session (cheap, sub-ms) and closes it in finally.
 */
export declare function neo4jSchemaFetcher(uri: string, user: string, password: string): Promise<SchemaFetcher>;
//# sourceMappingURL=schema-cache.d.ts.map