UNPKG

@struktoai/mirage-core

Version:

A Unified Virtual Filesystem For AI Agents

118 lines • 5.55 kB
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