import { type SpawnSyncOptionsWithStringEncoding, type SpawnSyncReturns } from "node:child_process";
/** One contributor's resolved Go source plus its target sub-package name. */
export interface ITtscBuildContributor {
    /** Sub-package suffix: scratch lands at `<host>/contrib/<name>/`. */
    name: string;
    /** Absolute path to the contributor's source directory. */
    source: string;
}
/** Source-plugin cache locations resolved for one ttsc invocation. */
export interface ITtscSourceBuildCachePaths {
    /** Root directory containing all ttsc-owned source build caches. */
    root: string;
    /** Directory containing content-addressed compiled plugin binaries. */
    pluginRoot: string;
    /** Directory passed to Go as `GOCACHE` for source-plugin builds. */
    goBuildRoot: string;
    /** How `goBuildRoot` was selected. */
    goBuildRootSource: "ttsc-cache" | "TTSC_GO_CACHE_DIR" | "GOCACHE";
}
/**
 * Build one Go source plugin into a cached executable.
 *
 * `opts.env` is the effective environment for this build — the caller merges `{
 * ...process.env, ...context.env }` so a programmatic `TtscCompiler` instance
 * can pin its own Go toolchain (`TTSC_GO_BINARY`), Go build cache
 * (`TTSC_GO_CACHE_DIR`), and Go build variables (`GOFLAGS`, `CGO_*`, …) without
 * mutating the shared `process.env`. CLI callers omit it and inherit
 * `process.env`, so ambient behavior is unchanged.
 */
export declare function buildSourcePlugin(opts: {
    source: string;
    pluginName: string;
    baseDir: string;
    cacheDir?: string;
    contributors?: readonly ITtscBuildContributor[];
    env?: NodeJS.ProcessEnv;
    label?: string;
    overlayDirs?: readonly string[];
    quiet?: boolean;
    ttscVersion: string;
    tsgoVersion: string;
}): string;
/** Opaque identity of one observed lock generation. */
export type PluginBuildLockFence = {
    protocol: "legacy" | "v2";
    generation: string;
};
/** Ownership token returned only to the process that acquired `current`. */
export type PluginBuildLockLease = {
    protocol: "v2";
    generation: string;
};
/**
 * Atomically acquire the current generation in a v2 coordination directory.
 *
 * A non-empty candidate is renamed to `current`. Directory rename cannot
 * replace a non-empty `current`, so exactly one contender wins without an
 * empty-owner publication window. `null` means either another v2 holder won or
 * the path is a legacy lock that must be observed before it can be reclaimed.
 *
 * Exported for deterministic multi-process tests.
 */
export declare function acquirePluginBuildLock(lockDir: string): PluginBuildLockLease | null;
/** Retire a held generation during the holder's `finally`. */
export declare function releasePluginBuildLock(lockDir: string, lease: PluginBuildLockLease): boolean;
/**
 * Retire exactly the generation carried by an abandoned observation.
 *
 * Exported for deterministic multi-process tests.
 */
export declare function reclaimPluginBuildLock(lockDir: string, fence: PluginBuildLockFence): boolean;
/**
 * Outcome of one waiting session on another process's plugin build lock.
 *
 * - `published`: the binary exists and can be reused.
 * - `released`: the observed generation no longer exists and no binary appeared —
 *   the holder freed the key normally, so the caller should retry ordinary
 *   acquisition without reporting or removing anything.
 * - `abandoned`: the lock still exists but is provably stale (dead owner, old
 *   legacy lock) or the wait budget expired; the caller may report and retire
 *   precisely the attached generation.
 *
 * Exported for unit tests.
 */
export type PluginBinaryWaitResult = {
    outcome: "published";
} | {
    outcome: "released";
} | {
    outcome: "abandoned";
    reason: string;
    fence: PluginBuildLockFence;
};
/**
 * Poll for the locked builder to publish its binary, up to `timeoutMs`.
 *
 * Exported for unit tests.
 */
export declare function waitForPluginBinary(opts: {
    binaryPath: string;
    lockDir: string;
    lockInfo: {
        label: string;
        pluginName: string;
        quiet: boolean;
    };
    timeoutMs: number;
}): PluginBinaryWaitResult;
/**
 * One observation of a plugin build lock directory's state.
 *
 * - `active`: the lock exists and its owner is alive (or cannot be disproven:
 *   another host, no metadata but young). Keep waiting.
 * - `abandoned`: the lock still exists and the evidence says nobody will ever
 *   release it — a same-host owner that is no longer running, or an old
 *   metadata-less legacy lock. Retiring its fenced generation is justified.
 * - `released`: the observed generation no longer exists. In v2 the persistent
 *   coordination root remains while `current` is absent. This is a routine
 *   handoff, never an infinitely old abandoned lock (issue #421).
 *
 * Exported for unit tests.
 */
export type PluginBuildLockObservation = {
    state: "active";
    owner: string;
    fence: PluginBuildLockFence;
} | {
    state: "abandoned";
    reason: string;
    fence: PluginBuildLockFence;
} | {
    state: "released";
};
/**
 * Classify the current state of a plugin build lock directory.
 *
 * Exported for unit tests.
 */
export declare function inspectPluginBuildLock(lockDir: string, now: number): PluginBuildLockObservation;
/**
 * Render a millisecond duration for lock diagnostics (`137ms`, `42s`, `9m 3s`).
 *
 * Total over every number: no caller produces a non-finite duration anymore
 * (the lock state machine reports "released" instead of an Infinity age), but
 * as defense in depth a non-finite input renders as `an unknown time` so no
 * public diagnostic can ever print `Infinitym NaNs` again (issue #421).
 *
 * Exported for unit tests.
 */
export declare function formatDuration(ms: number): string;
/**
 * Format an absolute filesystem path as a single `go.work`/`go.mod` token.
 *
 * The modfile grammar shared by `go.mod` and `go.work` (parsed by
 * `golang.org/x/mod/modfile`) is whitespace-tokenized, so a `use`/`replace`
 * path that contains a space — a home or project directory such as `/Users/John
 * Smith/...` or `C:\Users\John Smith\...` — must be emitted as a quoted string
 * or `go` cannot parse the generated `go.work`. Normalize Windows separators to
 * `/` (the workspace convention) and then delegate to
 * {@link autoQuoteGoModToken}, which mirrors `modfile.AutoQuote`.
 *
 * Separator normalization is itself a quoting trigger. A Windows UNC
 * (`\\server\share\...`) or extended-length (`\\?\C:\...`) path normalizes into
 * a token that starts with `//`, and the modfile lexer reads `//` as a line
 * comment wherever it appears. Emitted bare, such a token turns its whole
 * `use`/`replace` line into a comment: `go` exits 0, reports nothing, and the
 * overlay module simply disappears from the workspace.
 *
 * Exported for unit tests.
 */
export declare function formatGoWorkPath(p: string): string;
/**
 * Quote `token` for a `go.mod`/`go.work` line exactly as
 * `golang.org/x/mod/modfile`'s `AutoQuote` does: return it unchanged when it is
 * already a clean bare token, otherwise return its Go double-quoted form so the
 * value round-trips through the modfile lexer. A clean bare token is therefore
 * emitted byte-for-byte as before; only tokens that would otherwise be split or
 * interpreted as comments are quoted.
 *
 * Exported for unit tests.
 */
export declare function autoQuoteGoModToken(token: string): string;
export declare function spawnGoTool(goBinary: string, args: readonly string[], options: SpawnSyncOptionsWithStringEncoding): SpawnSyncReturns<string>;
/** Build the fixed cmd.exe switch sequence for one already quoted payload. */
export declare function windowsGoCommandArgs(payload: string): string[];
/**
 * Resolve the directory where compiled plugin binaries are cached.
 *
 * Delegates to {@link resolveSourceBuildCachePaths}; kept as a thin accessor for
 * callers (and tests) that only need the plugin-binary root. Triggers the
 * opportunistic project-cache GC as a side effect for the default location.
 */
export declare function resolvePluginCacheRoot(projectRoot: string, cacheDir?: string, env?: NodeJS.ProcessEnv): string;
/**
 * Resolve all source-plugin build cache directories for one invocation.
 *
 * `pluginRoot` stores compiled plugin binaries; `goBuildRoot` is the Go object
 * cache passed as `GOCACHE` while ttsc builds those binaries. Both live under a
 * single `root`, so persisting one directory covers the whole source-build
 * cache without depending on ttsc internals.
 */
export declare function resolveSourceBuildCachePaths(projectRoot: string, cacheDir?: string, env?: NodeJS.ProcessEnv): ITtscSourceBuildCachePaths;
/**
 * Return every directory `ttsc clean` should remove for `projectRoot`.
 *
 * Covers the resolved cache root (which holds `plugins/` and, when ttsc-owned,
 * `go-build/`), a ttsc-owned Go build cache that lives OUTSIDE that root
 * (`TTSC_GO_CACHE_DIR`), and the two legacy project-local caches. A
 * user-provided `GOCACHE` is never removed. Pure over `env`, so the CLI passes
 * `process.env` and a programmatic caller can pass an injected environment.
 */
export declare function resolveCleanTargets(projectRoot: string, cacheDir?: string, env?: NodeJS.ProcessEnv): string[];
/**
 * Machine-global cache directories created by pre-0.17 ttsc releases (XDG /
 * AppData / Library / `~/.cache`). ttsc no longer writes to any of these, but
 * an upgraded machine can still hold a multi-GB orphaned cache here, so `ttsc
 * clean` offers them for removal to reclaim that disk. Each entry is the whole
 * `<userCacheRoot>/ttsc` directory (both its `plugins` and `go-build`), which
 * was entirely ttsc-owned in those releases and is safe to remove.
 */
export declare function legacyGlobalCacheTargets(): string[];
/** Report whether `child` equals `parent` or is nested beneath it. */
export declare function isPathWithin(child: string, parent: string): boolean;
/**
 * Compute a deterministic SHA-256 cache key for a plugin build.
 *
 * The key covers every input that can produce a different binary: ttsc/tsgo
 * versions, platform, entry package, Go compiler identity, Go build environment
 * variables, overlay module sources, plugin source files, and contributor
 * source files. Contributors are sorted by name so declaration order does not
 * affect the key.
 *
 * Exposed for testing and for the `ttsc cache` CLI command.
 */
export declare function computeCacheKey(inputs: {
    contributors?: readonly ITtscBuildContributor[];
    dir: string;
    entry: string;
    env?: NodeJS.ProcessEnv;
    goBinary?: string;
    overlayDirs?: readonly string[];
    ttscVersion: string;
    tsgoVersion: string;
}): string;
