import { type ProjectInputPathIdentityContext } from "../../internal/projectInputPathIdentity";
import type { ITtscProjectInputSnapshot } from "../../structures/internal/ITtscProjectInputSnapshot";
import type { TtscBuildOptions } from "../../structures/internal/TtscBuildOptions";
type WatchTopologyOptions = Pick<TtscBuildOptions, "binary" | "emit" | "env" | "outDir" | "passthrough" | "projectRoot" | "tsconfig"> & {
    cwd: string;
    files: readonly string[];
};
type WatchTopologyCallbacks = {
    onError(location: string, error: unknown): void;
    onInputChange(change: WatchInputChange): void;
    onProjectInputWatchUnavailable?(roots: readonly string[]): void;
    onProjectInputWatchRoots?(roots: readonly string[]): void;
    onTopologyChange(): void;
};
export type WatchInputChange = {
    /** Keep the resident process but cold-load its compiler Program. */
    invalidate?: boolean;
    kind: "compiler" | "config" | "plugin" | "project";
    path?: string;
};
export type CompilerDirectoryWatchEventPlan = {
    changes: string[];
    rearm: string[];
    refresh: boolean;
};
/**
 * Keeps the launcher watch set aligned with the compiler's current program.
 *
 * TypeScript-Go's `--listFilesOnly` output is the authority for source and
 * declaration inputs. Configuration files, project-reference roots, and the
 * source trees of selected native plugins supplement that list, while compiler
 * outputs are filtered before any watcher is installed.
 */
export declare class WatchTopology {
    private readonly options;
    private readonly callbacks;
    private analysisOnly;
    private closed;
    private compilerPostRegistrationMembershipRefresh;
    private compilerPostRegistrationReconciliationScheduled;
    private compilerPostRegistrationSkipUnobservedProjectInputWatchRoots;
    private directories;
    private directoryWatchers;
    private extraInputs;
    private extraWatchers;
    private compilerFileSnapshots;
    private files;
    private fileWatchers;
    private observedDirectories;
    private outputFiles;
    private outputs;
    private projectInputFingerprints;
    private projectInputMatches;
    private projectInputs;
    private declaredProjectInputs;
    private projectInputRecoveryScheduled;
    private projectInputPostRegistrationReconciliationScheduled;
    private projectInputRejectedWatchRoots;
    private projectInputRequiredWatchRoots;
    private projectInputUnobservedWatchRoots;
    private projectInputWatchRoots;
    private projectInputWatchers;
    private projectInputLinkWatchers;
    private projectInputCompilerOutputOverlaps;
    private projectInputCompilerAcknowledgements;
    private reloadFiles;
    constructor(options: WatchTopologyOptions, callbacks: WatchTopologyCallbacks);
    /** Re-resolve compiler inputs and notify only when their membership changed. */
    refresh(notify: boolean): void;
    private refreshCompilerInputs;
    /**
     * Hand one Program-membership transition from the compiler lane to the
     * overlapping project-input lane.
     *
     * Windows can deliver the compiler membership refresh before the recursive
     * project watcher names the same creation. The rebuild scheduled here already
     * consumes the current project bytes, so publishing their strong fingerprints
     * keeps the later parent event from rediscovering the same population delta.
     * A newly tracked compiler file also remembers that fingerprint until its
     * first named content delivery; identical bytes are the delayed creation,
     * while different bytes are a real later edit and remain observable even
     * inside filesystem timestamp resolution.
     */
    private acknowledgeProjectInputCompilerMembership;
    /** Add Go plugin source trees discovered by the real build lane. */
    setExtraInputs(inputs: readonly string[]): void;
    /**
     * Reconcile project-rule dependencies, retaining absent files and empty glob
     * populations as live topology.
     */
    setProjectInputs(inputs: ITtscProjectInputSnapshot): void;
    /** Close every watcher so SIGINT/SIGTERM can drain the event loop. */
    close(): void;
    private syncFileWatchers;
    /** Compare a tracked file's content and physical owner with its snapshot. */
    private compilerFileMovement;
    private syncDirectoryWatchers;
    /**
     * Reconcile tracked compiler files after a newly registered watcher returns.
     *
     * A file or directory watcher can be returned before its backend is ready to
     * deliver the first event. The compiler-file stamps were captured before
     * registration, so one coalesced microtask can recover a change in that
     * handoff window. A real event updates the same stamp first and makes this
     * bounded scan a no-op.
     */
    private scheduleCompilerPostRegistrationReconciliation;
    /**
     * Narrow a plan's changes to the tracked files that actually moved.
     *
     * A backend that cannot name what changed forces the plan to nominate every
     * tracked file under the watched directory, which is the only safe answer it
     * can give from an event carrying no filename. macOS delivers such events for
     * ordinary activity elsewhere in the project, so the compiler lane would wake
     * for sources nobody touched. Only a content notification passes through: it
     * is the one event that claims the bytes moved. A rename claims the directory
     * entry was rewritten and an unnamed event claims nothing, so both are
     * decided from the bytes, which is the question neither of them answered.
     */
    private compilerChangesToReport;
    private recordCompilerFileSnapshot;
    private rearmFileWatchers;
    private syncExtraWatchers;
    private syncProjectInputWatchers;
    /**
     * Watch the directory that holds a declaration which is itself a link.
     *
     * A recursive watcher cannot report the link being replaced. The backend that
     * keys its handles by path skips an entry it already knows, and the handle it
     * put on the entry followed the link to the target's inode, which unlinking
     * and recreating the link never touches. A plain directory watch has neither
     * property: it reports the entry by name the moment it moves. These are kept
     * apart from the recursive roots because they are not roots — they observe
     * one directory, they are never reported as watch roots, and an ancestor
     * covering them does not make them redundant.
     */
    private syncProjectInputLinkWatchers;
    /** Drop the watcher that just reported a directory replacement. */
    private retireProjectInputWatcher;
    private retainProjectInputWatchRoot;
    /**
     * Retry a failed root on the next reconciliation instead of retiring it for
     * the session.
     *
     * This immediate recovery pass still honors the rejected root so it can
     * install a safe ancestor where one exists. Only the recovery fixpoint
     * reports a genuinely uncovered lane; transient gaps between fallback
     * candidates are not user-visible. The rejection then expires. A later
     * compiler refresh or an unchanged project-input republication can retry the
     * original root, while a permanently failing backend costs at most one
     * attempt per sync.
     */
    private scheduleProjectInputWatcherRecovery;
    /**
     * Reconcile the snapshot-to-watcher handoff after the caller's current turn.
     *
     * A recursive watcher can return before its backend is ready to deliver the
     * first event. The publication baseline is necessarily captured before that
     * watcher exists, so an input materialized synchronously after
     * `setProjectInputs()` would otherwise depend entirely on that startup event.
     * The ordinary fingerprint update makes this scan and a real backend event
     * race safely: whichever arrives first records the new population and the
     * other becomes a no-op.
     */
    private scheduleProjectInputPostRegistrationReconciliation;
    /**
     * Re-resolve declarations after watcher registration.
     *
     * A missing path can become a symlink before the handoff scan. The retained
     * normalized snapshot still names the pre-link spelling in that case, so a
     * scan can find the first target file without installing the physical owner
     * that must observe later target changes.
     */
    private refreshPublishedProjectInputIdentities;
    /** Drop retained owner choices for declarations no longer published. */
    private pruneProjectInputWatchRoots;
    /** Report only newly uncovered project-input roots as an observation loss. */
    private reportUnobservedProjectInputWatchRoots;
    /**
     * One snapshot holding every spelling of every declaration.
     *
     * Consumers that decide from a population rather than from a single path have
     * to see both, or half of them answer from the file a link pointed at when
     * the snapshot was published while the event they are judging resolved to the
     * file it points at now.
     */
    private projectInputPopulation;
    /**
     * Every spelling of one declaration that has to be anchored separately.
     *
     * The retained snapshot is normalized to physical identities, which is what
     * every comparison needs but not what every watcher needs: a declaration
     * reached through a symlink resolves to its target's directory, so anchoring
     * the normalized form alone watches the bytes and never the link. Retargeting
     * or replacing the link then goes unobserved, even though it is exactly what
     * decides which bytes the declaration names next. Both spellings are planned
     * through the same root selection, so the project-root hoist and the
     * nearest-existing-ancestor boundary still bound each of them, and the active
     * set drops one again whenever they coincide or share an ancestor.
     */
    private projectInputDeclarations;
    private projectInputWatchRoot;
    private refreshProjectInputs;
    private collectProjectInputMatches;
    private refreshFromDirectory;
    private isCompilerOutputDirectory;
    private isCompilerOutput;
    private isProjectInputCompilerOutputDirectory;
    private isProjectInputCompilerOutput;
    private isProjectInputDirectory;
    private classifyCompilerInput;
    private isPluginInput;
}
export declare function reloadInputsForFailedTopologyRefresh(reloadFiles: Iterable<string>, changed?: string): string[];
type SynchronizedWatcher = {
    close(): void;
    on(event: "error", listener: (error: Error) => void): unknown;
};
export declare function syncWatchers<T extends SynchronizedWatcher>(watchers: Map<string, T>, desired: ReadonlyMap<string, string>, create: (location: string, key: string) => T, onError: (location: string, error: unknown) => void, shouldContinue?: () => boolean): boolean;
export declare function literalGlobRoot(pattern: string): string;
/**
 * Chooses the one stable recursive watcher root owned by a project-input
 * declaration.
 *
 * Inputs inside the project share its physical root so directory replacement
 * cannot strand a child handle. External inputs use the nearest existing
 * ancestor of their declared parent, which is the explicit boundary for
 * observing a currently missing external tree without polling every file --
 * except that the boundary never rises to a directory holding the project,
 * since such a root outranks the project's own in the active merge and leaves
 * one handle over a shared system directory to carry everything. An external
 * declaration that can only be owned that way falls back to its own tree, and
 * is left unwatched when even that would contain the project.
 */
export declare function projectInputWatchDirectories(target: string, projectRoot: string): string[];
/**
 * Removes recursive roots already covered by an ancestor without rewriting the
 * declaration-specific roots retained by WatchTopology.
 */
export declare function projectInputActiveWatchDirectories(directories: Iterable<string>, identities?: import("../../internal/projectInputPathIdentity").FilesystemPathIdentityContext): string[];
export declare function projectInputAvailableWatchDirectory(location: string, rejected: ReadonlySet<string>, identities?: ProjectInputPathIdentityContext, projectRoot?: string): string | undefined;
/**
 * Whether a replacement at this path leaves a recursive watcher bound to the
 * object that was replaced.
 *
 * Only one backend needs the answer. Node routes a recursive watch to its own
 * per-directory implementation when the platform is neither macOS nor Windows,
 * and that implementation keys its handles by path: the handle for a directory
 * renamed away stays bound to the object that left, and any child whose name
 * survives the swap is skipped as already known. The native subtree backends
 * both other platforms use follow the path, so retiring their watcher would buy
 * nothing and would open a window in which no events are delivered.
 *
 * The answer is deliberately narrower than the rescan rule, because
 * reinstalling a root costs one watch descriptor per entry beneath it, which an
 * install storm would pay thousands of times. A directory appearing inside a
 * glob root deserves a rescan but replaces nothing a root stands on, and a
 * reload directory anchors the directory that contains it rather than itself,
 * since its fingerprint is a digest of its own immediate entries and nothing
 * below it can reach the declared corpus.
 */
export declare function projectInputReplacementStrandsWatchers(snapshot: ITtscProjectInputSnapshot, location: string, identities?: import("../../internal/projectInputPathIdentity").FilesystemPathIdentityContext, platform?: NodeJS.Platform): boolean;
/**
 * Decide whether an event that named no declared input can still have moved
 * one.
 *
 * The admitted set is the only bound on how often a watch session re-reads and
 * re-hashes its declared corpus, and both directions cost: too narrow drops an
 * atomic replacement, too wide re-fingerprints on every entry an install
 * creates. Exported so that boundary is pinned directly instead of being
 * inferred from a rebuild that a silent rescan and a skipped rescan produce
 * identically.
 */
export declare function projectInputTopologyMayAffect(snapshot: ITtscProjectInputSnapshot, location: string, previous: ReadonlyMap<string, string>, identities?: import("../../internal/projectInputPathIdentity").FilesystemPathIdentityContext): boolean;
export declare function projectInputEventShouldNotify(input: {
    contentChanged: boolean;
    directlyMatched: boolean;
    membershipChanged: boolean;
}): boolean;
/**
 * Classify an exact execution-selection input ahead of ordinary project data.
 *
 * `changedInputs` carries fingerprint or membership deltas, so a filename-less
 * event can still select the cold lane. A named exact event selects the cold
 * lane only after the surrounding change detector admits the event; unchanged
 * bytes remain quiet before this classifier is observed.
 */
export declare function projectInputReloadEventShouldNotify(input: {
    causedBy?: readonly string[];
    changed?: string;
    changedInputs: readonly string[];
    reloadDirectories?: readonly string[];
    reloadFiles: readonly string[];
    globs?: readonly string[];
}): boolean;
/**
 * Return whether a project-input population transition can reshape a Program.
 *
 * JSON is data to a ProjectRule but may simultaneously be a `resolveJsonModule`
 * source. TypeScript and JavaScript paths can likewise overlap a project-input
 * declaration. Their creation or deletion therefore requires a cold Program
 * inside the existing resident process. A filename-less event cannot identify
 * the changed member and is conservatively invalidating whenever the population
 * moved.
 */
export declare function projectInputMembershipInvalidatesProgram(input: {
    changed?: string;
    changedInputs?: readonly string[];
    contentChanged?: boolean;
    next: ReadonlyMap<string, string>;
    previous: ReadonlyMap<string, string>;
}): boolean;
/**
 * Plan one compiler-directory event without relying on backend timing.
 *
 * POSIX file watchers own ordinary content changes. A named rename re-arms the
 * replaced file; an unnamed event conservatively re-arms and reports every
 * surviving tracked input below the watch root. Windows has no per-file
 * watchers here, so both named and unnamed directory events report inputs.
 */
export declare function planCompilerDirectoryWatchEvent(input: {
    changed?: string;
    event: string;
    exists(location: string): boolean;
    location: string;
    platform: NodeJS.Platform;
    trackedFiles: ReadonlyMap<string, string>;
}): CompilerDirectoryWatchEventPlan;
export {};
