import { PathSpec, type ContentType } from '../../types.ts';
import { type Codec } from './codec.ts';
export declare const ROOT = "root";
export declare const INVALID = "invalid";
/**
 * One dynamic segment of a scope: the key the decoded value is stored under,
 * and how the segment encodes it. `idKey` is set for `<label>__<id>`
 * composite segments: the decoded payload splits on its LAST `__` (so a
 * three-part `KEY__name__id` keeps `KEY__name` as the label), the label
 * stored under `name` and the id under `idKey`. A payload with no `__` or an
 * empty half does not match the scope.
 *
 * `idSplit: 'first'` cuts on the FIRST `__` instead, for a label that can
 * never hold one and an id that can: a Google app's title is sanitized, which
 * collapses every run of underscores, while a Drive file id may carry `__`
 * itself (`1D2dqZx96t_aSHk3kLorY__OWES6…`), and the last cut would read
 * half of it as the label.
 *
 * A `variadic` slot stands for a run of one or more consecutive segments
 * instead of exactly one. Segments before it anchor at the start of the
 * path, segments after it at the end, every segment in the run must decode,
 * and the stored value comes from the run's DEEPEST segment: notion's pages
 * nest arbitrarily, and `pages/a__1/b__2/page.json` stores `page=b,
 * page_id=2` because the innermost page is the one the path addresses. At
 * most one variadic slot per scope.
 */
export declare class Slot {
    readonly name: string;
    readonly codec: Codec;
    readonly idKey: string | null;
    readonly variadic: boolean;
    readonly idSplit: 'first' | 'last';
    constructor(name: string, codec?: Codec, idKey?: string | null, variadic?: boolean, idSplit?: 'first' | 'last');
}
export type Segment = string | Slot;
/**
 * One addressable position in a fixed API hierarchy.
 *
 * `kind` is the position's name; listers, probes and readers key on it.
 * `segments` is the path shape, literals and slots. `leaf` marks a file
 * rather than a directory, `filetype` its rendered content type. `probed` is whether
 * stat must prove existence (parent listing by default); false for positions
 * that exist by construction, like the top-level directories.
 */
export declare class Scope {
    readonly kind: string;
    readonly segments: readonly Segment[];
    readonly leaf: boolean;
    readonly filetype: ContentType | null;
    readonly probed: boolean;
    constructor(init: {
        kind: string;
        segments: readonly Segment[];
        leaf?: boolean;
        filetype?: ContentType;
        probed?: boolean;
    });
}
/**
 * Where in the hierarchy a path landed.
 *
 * `kind` is the matched scope's kind, or `root`/`invalid`. `slots` holds
 * the decoded dynamic segments by name. `vfsPath` is the raw path that
 * was classified. `scope` is the matched scope; null for root and invalid.
 */
/**
 * Where in the hierarchy a path landed.
 *
 * `pattern` is the glob the line typed for the directory's children, set only
 * for a kind named in `patternKinds` and null everywhere else. A lister whose
 * listing is a window moves that window to the span the glob asks for instead
 * of filtering its own; every other consumer ignores the field, the way a
 * command ignores the `Context` facts it does not read.
 *
 * `limit` is the most entries a walk can still keep of the directory's
 * listing, absent outside a walk. A lister that pages may stop once it holds
 * that many and answer a partial `DirListing`; any other ignores it, as it
 * does `pattern`.
 */
export interface ScopeMatch {
    readonly kind: string;
    readonly vfsPath: string;
    readonly slots: Record<string, string>;
    readonly scope: Scope | null;
    readonly pattern: string | null;
    readonly limit?: number;
}
export type DetectFn = (path: PathSpec | string) => ScopeMatch;
/** Decode one path segment through a slot, null when it does not fit. */
export declare function decodeSlot(slot: Slot, part: string): Record<string, string> | null;
/** The scope's variadic slot and its position, null when it has none. */
export declare function variadicSlot(segments: readonly Segment[]): [number, Slot] | null;
/** Match path segments against the table, first declared scope wins. */
export declare function matchScope(scopes: readonly Scope[], parts: readonly string[]): [Scope, Record<string, string>] | null;
/**
 * Build a path classifier from a scope table.
 *
 * The classifier is the single description of the backend's tree: readdir,
 * stat, read, and any search push-down all dispatch on its result, so the
 * file surface and the command surface cannot disagree about what a path
 * means. Hidden segments classify as invalid, which every consumer turns
 * into ENOENT. Scopes are matched in declaration order.
 *
 * Postgres's table, classified level by level:
 *
 *     path                             kind         slots
 *     /                                root         {}
 *     /public                          schema       {schema}
 *     /public/tables                   kind         {schema, kind}
 *     /public/tables/books             entity       {schema, kind, entity}
 *     /public/tables/books/rows.jsonl  entity_rows  {schema, kind, entity}
 *
 * `kind` names the level a path landed on; the slots identify the branch
 * taken at each dynamic level above it. Literal levels (`rows.jsonl`)
 * contribute no slot, and one dynamic level can contribute two (`idKey`).
 */
export declare function makeDetectScope(scopes: readonly Scope[]): DetectFn;
//# sourceMappingURL=scope.d.ts.map