import type { OnProgress } from '../../progress-event.js';
import type { CodeFileGuardrails } from '../parsers.js';
import type { LineageState } from './lineage.js';
import type { GrammarManifestEntry } from './grammar-manifest.js';
import type { BlobEntities, EntityBounds, EntityCommitRecord, EntityCounters, EntityDiagnostic, EntityRecord, LineageEvent, LineageThresholds } from './types.js';
/**
 * Parse results keyed by blob id and path.
 *
 * A blob id addresses content, not a path or a commit, so an entry stays valid
 * across renames, rebases, and reverts — the cache never needs invalidating,
 * only bounding.
 */
export interface BlobEntityCache {
    get(blobSha: string, relativePath: string): BlobEntities | undefined;
    set(value: BlobEntities): void;
    /** Everything currently held, for persistence. */
    entries(): Iterable<BlobEntities>;
}
/**
 * Extraction is addressed by content *and* path, never by content alone.
 *
 * Git stores one blob per distinct content, so two byte-identical files — an
 * empty `__init__.py`, a duplicated barrel, a generated stub — share a single
 * blob id. Entity records carry the path they came from, so keying on the blob
 * alone would hand the second file the first file's records, and every entity
 * in it would be attributed to the wrong file. Reads are still deduplicated by
 * blob id; only extraction is per pair.
 */
export declare function blobEntityKey(blobSha: string, relativePath: string): string;
export declare function createMemoryBlobCache(initial?: Iterable<BlobEntities>): BlobEntityCache;
/** A path and the blob that held its content at some revision. */
export interface BlobRequest {
    blobSha: string;
    relativePath: string;
}
export interface ReadBlobEntitiesOptions {
    repoRoot: string;
    bounds: EntityBounds;
    guardrails?: CodeFileGuardrails;
    cache?: BlobEntityCache;
    signal?: AbortSignal;
    counters?: EntityCounters;
    /** Running total across a whole index build, for `maxTotalBlobBytes`. */
    budget?: {
        bytesRead: number;
        seenOids?: Set<string>;
    };
    /** Optional successful body cache for bounded multi-transition observers. */
    bodyCache?: Map<string, Buffer>;
    /** Materialize every requested blob but extract entities only for selected paths. */
    shouldExtract?: (relativePath: string) => boolean;
}
export interface ReadBlobEntitiesResult {
    /** Keyed by `blobEntityKey(blobSha, relativePath)`, not by blob id alone. */
    entities: Map<string, BlobEntities>;
    /** Decoded blob text by blob id, for the similarity rung. */
    contents: Map<string, string>;
    /** Exact bounded bytes by blob id, reused by whole-file proof without a second Git read. */
    buffers: Map<string, Buffer>;
    diagnostics: EntityDiagnostic[];
    /** Paths whose physical blob evidence could not be observed. */
    evidenceUnavailablePaths: Set<string>;
    /**
     * Blobs no extractor could read, counted by file extension.
     *
     * Returned rather than turned into diagnostics here: a commit walk calls this
     * once per commit, and one diagnostic per commit per extension would bury the
     * bounds that actually matter. Callers aggregate over their whole run and
     * emit a single `blob-unsupported`.
     */
    unsupported: Map<string, number>;
}
/**
 * One diagnostic for every extension the run could not read, or none.
 *
 * A silent skip is indistinguishable from "this file has no entities", and a
 * consumer reading an empty result has no way to tell an unsupported language
 * from an empty one.
 */
export declare function unsupportedDiagnostic(unsupported: ReadonlyMap<string, number>): EntityDiagnostic | undefined;
/**
 * Turn a set of (path, blob) pairs into entity records.
 *
 * Ordinary index construction reads contents even on an entity-cache hit, so
 * matching sees the same evidence whether that cache is warm or cold — a
 * matcher whose result depends on cache state produces history that cannot be
 * reproduced. Bounded all-parent observers may additionally supply a run-local
 * immutable body cache; that cache avoids duplicate reads of the same OID but
 * never changes the bytes supplied to matching. The persistent entity cache
 * still skips only the expensive half, parsing, and the counters report reads
 * and parses separately.
 */
export declare function readBlobEntities(requests: readonly BlobRequest[], options: ReadBlobEntitiesOptions): Promise<ReadBlobEntitiesResult>;
export interface SnapshotOptions extends ReadBlobEntitiesOptions {
    /** Blobs already known for this revision, to skip the tree listing. */
    entries?: readonly BlobRequest[];
}
export interface EntitySnapshot {
    byPath: Map<string, EntityRecord[]>;
    /** Decoded file text by path. */
    contents: Map<string, string>;
    diagnostics: EntityDiagnostic[];
    /** Coverage gaps by extension, for the caller to aggregate and report once. */
    unsupported: Map<string, number>;
}
/**
 * Every entity in one revision.
 *
 * Seeds a bounded walk at its window boundary, and later lets entity diff
 * describe a revision that is not a commit at all — the index, or a tree.
 */
export declare function snapshotEntities(ref: string, options: SnapshotOptions): Promise<EntitySnapshot>;
export interface IndexHistoryOptions {
    repoRoot: string;
    repoIdentity: string;
    state: LineageState;
    /** Live entities per path; mutated in step with `state`. */
    byPath: Map<string, EntityRecord[]>;
    head?: string;
    /** Exclusive lower bound — the head a previous index run reached. */
    sinceCommit?: string;
    /** Where this walk's commit numbering starts; nonzero when extending an index. */
    orderOffset?: number;
    bounds?: EntityBounds;
    thresholds?: LineageThresholds;
    guardrails?: CodeFileGuardrails;
    cache?: BlobEntityCache;
    signal?: AbortSignal;
    counters?: EntityCounters;
    onProgress?: OnProgress;
}
export interface IndexHistoryResult {
    commits: EntityCommitRecord[];
    events: LineageEvent[];
    diagnostics: EntityDiagnostic[];
    truncated: boolean;
    /**
     * The extension could not be joined to the state it was extending.
     *
     * Set only when a `sinceCommit` walk hit the commit bound, which means the
     * commits adjacent to the cached head were dropped. Nothing is returned in
     * that case: the caller must rebuild rather than splice a hole.
     */
    gapped?: boolean;
}
type GrammarVerifier = (entry: GrammarManifestEntry) => Promise<boolean>;
/**
 * Resolve and physically verify the bounded grammar set before any commit is
 * matched. The verifier parameter is a test seam only; `indexHistory` always
 * uses the digest-pinned physical verifier.
 */
export declare function preverifyLineageGrammars(relativePaths: Iterable<string>, verify?: GrammarVerifier): Promise<EntityDiagnostic[]>;
/**
 * Walk a commit window, matching each commit's entities against the running
 * lineage state.
 *
 * The walk is first-parent and linear, which is what lets the running state
 * serve as the parent snapshot: no full tree is ever materialised per commit.
 */
export declare function indexHistory(options: IndexHistoryOptions): Promise<IndexHistoryResult>;
export {};
