import type { Accessor } from '../../accessor/base.ts';
import type { FileCacheMixin } from '../../cache/file/mixin.ts';
import type { Evicted, IndexConfig } from '../../cache/index/config.ts';
import { IOConfig } from '../../io/config.ts';
import type { MemoryBudget } from '../../io/memory.ts';
import { type NetworkRule, type NetworkSetting } from '../../io/network.ts';
import type { Observer } from '../../observe/observer.ts';
import { type Decisions, Policies } from '../../policy/index.ts';
import type { ProcessView } from '../../process/view.ts';
import type { DiffComputer } from '../../rs/diff_types.ts';
import type { Runtime } from '../../runtime/base.ts';
import type { DispatchFn } from '../../runtime/types.ts';
import { Limit, MountMode, PathSpec, type ReadSpec, WritePolicy } from '../../types.ts';
import { type Clock } from '../../utils/clock.ts';
import { type IdSource } from '../../utils/ids.ts';
import type { BaseVFS } from '../../vfs/base.ts';
import type { SessionAccess } from '../../view/types.ts';
import { CLIRegistry } from '../cli/registry.ts';
import type { SessionState } from '../session/session.ts';
import type { MountRow } from '../types.ts';
import { MountEntry } from './mount.ts';
interface ReadReconciler {
    mayServeListing(mount: MountEntry, folder: string, version: string | null): Promise<boolean>;
    onGone(gone: readonly Evicted[], excluded?: readonly string[]): Promise<void>;
}
export declare const DEV_PREFIX = "/dev/";
export declare const logger: {
    debug?: (message: string) => void;
};
export declare class MountCommandUnsupported extends Error {
    readonly cmdName: string;
    readonly backend: string;
    readonly operand: string;
    constructor(cmdName: string, backend: string, operand: string);
}
/** The placements of the mounts a registry is constructed with, keyed by raw prefix. */
export interface RegistryPlacements {
    index?: IndexConfig;
    indexes?: Record<string, IndexConfig>;
    refs?: Record<string, string>;
    /** The write policy a mount takes when it names none. */
    defaultWrite?: WritePolicy;
    /** Each mount's own write policy, keyed by raw prefix. */
    writes?: Record<string, WritePolicy>;
    /** Scopes the redis index stores this registry builds. */
    workspaceId?: string;
}
export declare class MountRegistry {
    readonly io: IOConfig;
    readonly clock: Clock;
    readonly ids: IdSource;
    diff?: DiffComputer;
    private readonly indexConfig;
    private readonly workspaceId;
    processView?: (session: SessionState) => ProcessView;
    fillEnv?: (session: SessionState, names: ReadonlySet<string>) => Promise<void>;
    observer: Observer | null;
    private readonly mountList;
    private rowCache;
    readonly retiringMounts: Map<BaseVFS<Accessor>, Promise<void>>;
    readonly retiredMounts: WeakSet<BaseVFS<Accessor>>;
    private rootRef;
    private defaultRead;
    private defaultWrite;
    private cacheStore;
    private reconciler;
    private dispatchFn;
    private dispatchAsFn;
    runtimeBindings: Readonly<Record<string, Runtime | undefined>>;
    runtimeEntries: readonly Runtime[];
    commandLimits: Readonly<Record<string, Limit>>;
    memory: MemoryBudget | null;
    network: NetworkRule | null;
    systemClock: Clock | null;
    private readonly narrowed;
    private watch;
    readonly policies: Policies;
    /** The approval ledger an Ask is taken to, which the policies hold
     * (`Policies.decisions`) so a session write reaches it too. */
    get decisions(): Decisions;
    readonly clis: CLIRegistry;
    private clocksBound;
    /**
     * Drop what a write past the dispatcher may have made stale.
     *
     * Native code (an external program, a remote runtime line) or an account
     * CLI changed its service with no path the dispatcher saw, so per-path
     * invalidation has nothing to aim at: after `gws sheets spreadsheets
     * create` the new file has no cache entry to expire, which is exactly the
     * case that matters. Every mount the write may have reached drops what it
     * keeps (`MountEntry.dropCaches`). A write verb is rare next to reads, and
     * the cost is one cold listing on a mount's next read, never a wrong
     * answer. Mirrors Python's `MountRegistry.drop_caches`.
     *
     * @param by the client that wrote, whose `reaches` names the mounts it may
     *   have changed; null for every mount.
     */
    dropCaches(by?: Accessor | null): Promise<void>;
    /**
     * Land a runtime's writes when its line ends, however it ended, and drop
     * the caches when they went past the dispatcher.
     *
     * A runtime serving the workspace over SFTP sent each write through the
     * dispatcher, which invalidated that path as it went
     * (`fileEffectsDispatched`). Any other may have written anywhere in its
     * view of the workspace, so every mount drops its caches and the next
     * command refetches instead of serving pre-line state: `cat /data/x`
     * caches "old", `python3 job.py` writes "new" straight to S3 through the
     * sandbox's own FUSE mount, and without the drop the next `cat /data/x`
     * would serve the stale "old". Mirrors Python's `MountRegistry.settle`.
     */
    settle(runtime: Runtime): Promise<void>;
    /**
     * The rule a session's lines are held to: the workspace's, narrowed by the
     * `network` its profile states, its routes over the workspace's, built once
     * per setting. Mirrors Python's `MountRegistry.network_of`.
     */
    networkOf(setting: NetworkSetting | null): NetworkRule | null;
    /** Set what starts judging the process's connections, once a rule needs it. */
    setNetworkWatch(watch: () => void): void;
    /** The rules `networkOf` built, whose connections close with the workspace. */
    narrowedNetworks(): NetworkRule[];
    setReconciler(reconciler: ReadReconciler): void;
    /**
     * The workspace's dispatcher, which a shell glob lists and stats through;
     * null until the workspace wires it in, and for a bare registry with no
     * workspace behind it. Mirrors Python's `MountRegistry.dispatch`.
     */
    get dispatch(): DispatchFn | null;
    /**
     * The workspace's dispatcher bound to `owner`, the ambient one without. On
     * the browser's fallback storage the ambient session is whichever line ran
     * last, so a glob lists as its own line's session only through a dispatch
     * bound to it. Python's task context keeps each line's session, so it has no
     * twin.
     */
    dispatchFor(owner?: SessionAccess): DispatchFn | null;
    /**
     * Wire in the workspace's dispatcher, and how to bind it to a session.
     * Mirrors Python's `set_dispatch`.
     */
    setDispatch(dispatch: DispatchFn, dispatchAs?: (owner: SessionAccess) => DispatchFn): void;
    /**
     * Attach the workspace file cache and build per-mount CacheManagers.
     * Called once by Workspace after the cache store exists; mounts
     * added later get their manager in `mount()`. The cache is a hidden
     * store, never a mount.
     */
    attachFileCache(cache: FileCacheMixin | null): void;
    /**
     * Run the shared listing verdict for one mount's cached listing.
     *
     * The reconciler is read at call time because `attachFileCache` runs
     * before `setReconciler`, and a manager with none trusts its listings. A
     * retiring mount answers false without asking. Python also answers false
     * for EBUSY from a mount that began retiring mid-check; this side's probe
     * never enters `mount.use()`, so the `retiring` check is the whole guard.
     */
    private mayServeListing;
    private attachManager;
    constructor(mounts: Record<string, BaseVFS>, defaultMode: MountMode, modeOverrides?: Record<string, MountMode>, defaultRead?: ReadSpec, readOverrides?: Record<string, ReadSpec>, placements?: RegistryPlacements, io?: IOConfig, clock?: Clock, ids?: IdSource);
    /**
     * Build one mount with everything the tree needs to run its driver:
     * the driver's command and op tables registered on the entry, the
     * general commands beside them. The constructor and `mount()`
     * both place through here, so a mount is the same whichever entry point
     * built it. Mirrors the Python registry, whose constructor mounts
     * through `mount()` as well.
     */
    private static place;
    /**
     * The store a driver runs under: a new one from the mount's or the
     * workspace's index config, else a RAM store at the driver's own TTL.
     */
    private indexFor;
    /**
     * Refuse a mount that would place one VFS object at a second prefix, or a
     * disk on a folder a mounted disk already has.
     * Mirrors Python's `check_storage_unshared`.
     *
     * @throws SharedStorageError naming the mount already there.
     */
    checkStorageUnshared(prefix: string, vfs: BaseVFS, mounts: readonly MountEntry[]): void;
    /** A removed VFS instance cannot start a second lifecycle. */
    checkVfsAvailable(vfs: BaseVFS): void;
    /**
     * Add a mount dynamically. Mirrors Python's `registry.mount(...)`.
     * Registers the VFS's commands and ops on the new mount and
     * re-sorts mounts by prefix length (longest first). Once the workspace is
     * built, the VFS takes the workspace clock before anything is placed.
     *
     * @throws Error when the prefix is taken, or another workspace holds the
     *   VFS on another clock.
     */
    mount(prefix: string, vfs: BaseVFS, mode?: MountMode, read?: ReadSpec, placement?: {
        index?: IndexConfig;
        vfsRef?: string | null;
        /** The mount's write policy; undefined takes the workspace default. */
        write?: WritePolicy;
    }): MountEntry;
    /**
     * Hand every mount's and CLI install's accessor the workspace clock, once
     * the workspace is built; a mount placed later takes it as it is placed. Mirrors Python's
     * `MountRegistry.bind_clocks`.
     *
     * @throws Error when another workspace holds one of the VFSes on another
     *   clock; nothing stays bound.
     */
    bindClocks(): void;
    /**
     * Let go of the workspace clock on `vfs`, as an unmounted mount finishes its
     * last call. Mirrors Python's `MountRegistry.release_clock`.
     */
    releaseClock(vfs: BaseVFS): void;
    /**
     * Let go of the workspace clock on every mount and CLI install, as the
     * workspace closes or fails to come to be, so another workspace may mount
     * its VFSes on its own. Mirrors Python's `MountRegistry.release_clocks`.
     */
    releaseClocks(): void;
    /**
     * Remove a mount by exact prefix. Mirrors Python's `registry.unmount(...)`.
     * Per-mount commands and ops live on the Mount instance and die with it.
     * The /dev/ mount is reserved and cannot be removed.
     */
    unmount(prefix: string): MountEntry;
    /**
     * The mount at exactly this prefix; throws noMount for none. Callers
     * that expect the miss branch on `tryMountForPrefix` returning null.
     */
    mountForPrefix(prefix: string): MountEntry;
    /** The mount at exactly this prefix, or null when none matches. */
    tryMountForPrefix(prefix: string, includeHidden?: boolean): MountEntry | null;
    /**
     * One mount's configured cap for a command or op name. The lookup a
     * command's output guard resolves with; tolerant of a prefix that matches
     * no mount (unmounted between stamp and boundary) by answering null.
     */
    /**
     * Set the mount's `walkLimit`, which decides how its backend searches (its
     * own search API under a cap, every file without): the mount's `@all` entry,
     * else the workspace's, else the backend's default, a null one lifting the
     * cap. A command's own `walkLimit` changes only how far that command walks.
     * Mirrors Python's `settle_walk_limit`.
     */
    settleWalkLimit(entry: MountEntry): void;
    /**
     * The mount a command's output guard resolves a stamped prefix to; null
     * for a prefix that matches no mount (unmounted between stamp and
     * boundary). Mirrors Python's `limit_mount`.
     */
    limitMount(prefix: string): MountEntry | null;
    isMountRoot(path: string): boolean;
    descendantMounts(path: string): MountEntry[];
    mountPrefixes(): string[];
    get rootMount(): MountEntry | null;
    get fileCache(): FileCacheMixin | null;
    resolve(path: string): [BaseVFS, PathSpec, MountMode];
    /**
     * The mount that handles this path; throws noMount for none.
     *
     * The lookup contract pair: `mountFor` is for callers whose path must
     * be mounted (a miss is a broken invariant and propagates as a typed
     * noMount error), `tryMountFor` is for callers with a real fallback
     * for the miss. Never catch around this method — call the try variant
     * instead.
     */
    mountFor(path: string): MountEntry;
    /** The mount that handles this path, or null when none does. */
    tryMountFor(path: string): MountEntry | null;
    /**
     * One row per registered mount, for the namespace reader the dispatcher
     * hands out; built once per change to the mount list. The functions a VFS
     * answers are the ones its mount read when it was mounted
     * (`MountEntry.calls`); who is told of a mount is read off the mount when
     * asked, since a session's documents mount is narrowed after it is made.
     * Mirrors Python's `mount_rows`.
     */
    mountRows(): MountRow[];
    /** The mounts the session a call through a facade of `sessionId` runs as
     * (null: the default facade) is told of at all, hides aside. */
    visibleMounts(sessionId?: string | null): MountEntry[];
    allMounts(): readonly MountEntry[];
    /**
     * The bytes the workspace's RAM mounts hold, each store counted once, as a
     * remount's retiring entry shares its store. Mirrors Python's
     * `held_in_memory`.
     */
    heldInMemory(): number;
    isExecAllowed(access?: SessionAccess): boolean;
    /**
     * Whether code may be loaded from this path: the per-script form of
     * `isExecAllowed`, read by an interpreter running a file operand
     * (`python3 path.py`, `js app.js`, or a `./script` whose shebang names
     * one). Shell scripts (`bash script.sh`, `source`, a `./script` with no
     * shebang or an sh one) never ask: they run in the shell, which checks
     * each of their commands like a typed one.
     */
    execAllowedAt: (virtual: string, access?: SessionAccess) => boolean;
    private fallbackCandidates;
    mountForCommand(cmdName: string, access?: SessionAccess): MountEntry | null;
    /**
     * How many leading words form a registered command name. Command names
     * may span several words ("gws docs documents get"), git-style. A nested
     * name resolves from anywhere its owning mount is reachable, so this
     * scans every mount (mirroring mountForCommand) and returns the longest
     * prefix any mount recognises, or 1 (bare first token) when none does.
     */
    matchCommandPrefix(words: string[]): number;
    /**
     * Candidates for discovery and admission, without readying a backend.
     * A path or working directory that owns the command selects it; fallback
     * retains root-first, then longest-prefix registration order. A bound
     * mount stays authoritative so the dispatcher owns visibility refusals.
     */
    commandCandidates(cmdName: string, pathScopes: readonly PathSpec[], cwd: string, access?: SessionAccess): MountEntry[];
    /** The first discovery candidate; selecting execution may refuse ambiguity. */
    commandMount(cmdName: string, pathScopes: readonly PathSpec[], cwd: string, access?: SessionAccess): MountEntry | null;
    /** The ordered execution candidates and grammar for the same invocation. */
    private invocationChoices;
    /** The targets this invocation can run, before fetching its environment. */
    invocationCandidates(cmdName: string, pathScopes: readonly PathSpec[], cwd: string, argv?: readonly string[], access?: SessionAccess): MountEntry[];
    /** The grammar used to classify this invocation, without refusing ambiguity. */
    invocationMount(cmdName: string, pathScopes: readonly PathSpec[], cwd: string, argv?: readonly string[], access?: SessionAccess): MountEntry | null;
    /** Select an unambiguous write target without starting a backend. */
    executionMount(cmdName: string, pathScopes: readonly PathSpec[], cwd: string, argv?: readonly string[], access?: SessionAccess): MountEntry | null;
    resolveMount(cmdName: string, pathScopes: readonly PathSpec[], cwd: string, argv?: readonly string[], access?: SessionAccess): Promise<MountEntry | null>;
}
export {};
//# sourceMappingURL=registry.d.ts.map