@struktoai/mirage-core
Version:
A Unified Virtual Filesystem For AI Agents
118 lines • 5.55 kB
TypeScript
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