import type { MountSpec, SqlParam } from "./types.js"; /** * Pre-compiled mount set: an allow-list of workspace subtrees this instance is * restricted to. Unlike `exclude` (a deny-list), mounts are default-deny — a * path is only visible if it lives inside a mount or is an ancestor directory * leading down to one. * * This is what lets a single `PgFileSystem` over one workspace present a * scoped per-user view (`/users/` + `/general` + a few `/clienti/`) * without remapping paths or wrapping the instance in a router. * * Mount roots are grouped by depth so the cost of a check scales with the * number of distinct root depths, not the number of mounts (a view can carry * thousands of sibling mounts like `/clients/`). A path is inside a mount * iff its prefix at some root depth is one of the roots at that depth, and it * is an ancestor of a mount iff it is in the precomputed set of strict root * ancestors. In SQL that becomes one hashed set-membership test per depth: * * subpath(path, 0, d1)::text IN (SELECT unnest($a::text[])) -- depth d1 * OR subpath(path, 0, d2)::text IN (SELECT unnest($b::text[])) -- depth d2 * OR path::text IN (SELECT unnest($anc::text[])) -- ancestors * * The `IN (SELECT unnest(...))` form plans as a hashed SubPlan even under a * generic plan for a reused prepared statement, where `= ANY($n)` would fall * back to a linear scan of the array per row. Comparing as text keeps the * hashing independent of the ltree extension version. */ export interface CompiledMounts { empty: boolean; /** True when `/` itself is mounted: every path is visible, only writes are scoped. */ coversAll: boolean; /** Internal POSIX mount root -> readonly flag. */ byRoot: ReadonlyMap; /** Distinct root depths (path segment count), ascending. */ depths: number[]; /** Internal POSIX mount roots grouped by depth. */ rootsByDepth: ReadonlyMap>; /** Strict ancestors of every mount root, as internal POSIX paths. */ ancestors: ReadonlySet; /** ltree-encoded roots per depth; `nlevel` counts the workspace label. */ ltreesByDepth: Array<{ nlevel: number; ltrees: string[]; }>; /** ltree-encoded strict ancestors of every mount root. */ ancestorLtrees: string[]; } export declare function emptyMounts(): CompiledMounts; /** * Compile a list of mount specs into the form used by `mountWhereSql()` and * the `mountVisible`/`mountWritable` predicates. Each spec's `path` is resolved * relative to `rootDirInternal` (normally `/`, i.e. identity). * * Returns the shared empty singleton when there are no specs, or when a writable * root mount is the only meaningful restriction. Readonly root mounts and * readonly children under a writable root still matter for write guards, so * those remain non-empty even though they expose the whole tree. */ export declare function compileMounts(specs: MountSpec[] | undefined, rootDirInternal: string, workspaceId: string): CompiledMounts; /** * True when `internalPath` is inside a mount, or is an ancestor directory of * one (so intermediate dirs like `/users` and `/clienti` remain walkable, and * `/` always lists the branches that lead to mounts). */ export declare function mountVisible(c: CompiledMounts, internalPath: string): boolean; /** * True when `internalPath` is inside a non-readonly mount. The deepest * enclosing mount decides, so a readonly mount nested under a writable one (or * the reverse) carves out its own subtree. */ export declare function mountWritable(c: CompiledMounts, internalPath: string): boolean; /** * Build the SQL fragment that, ANDed into a `WHERE` clause, keeps only entries * inside a mount or on an ancestor path leading to one. Returns `TRUE` (no-op) * when no mounts are configured or `/` itself is mounted. * * `pathExpr` names the ltree path column (e.g. `e.path`). Emits one `text[]` * param per distinct root depth, plus one for the ancestor set when non-empty. * `subpath` clips to the path's own length, so a path shallower than a depth * yields a prefix that can never equal a root at that depth. */ export declare function mountWhereSql(c: CompiledMounts, pathExpr: string, nextParamIdx: number): { sql: string; params: SqlParam[]; }; //# sourceMappingURL=mounts.d.ts.map