import "@effected/glob"; import "@effected/lockfiles"; import "@effected/package-json"; import "effect"; //#region src/WorkspacesSync.d.ts /** * The synchronous file operations the sync entry points need, supplied by the * consumer. Node's built-ins satisfy it directly: * * ```ts * import { existsSync, readFileSync, readdirSync, statSync } from "node:fs"; * * const fileSystem: SyncFileSystem = { * exists: existsSync, * readFile: (p) => readFileSync(p, "utf8"), * readDirectory: (p) => readdirSync(p), * isDirectory: (p) => statSync(p).isDirectory(), * }; * ``` * * `exists` must return a boolean and not throw (Node's `existsSync` never * does). The other three may throw — `statSync` on a missing path, a * permission error mid-read — and every throw is absorbed into the documented * degraded-skip semantics: a throwing `readFile` reads as an unusable * manifest, a throwing `readDirectory` as an unreadable directory, a throwing * `isDirectory` as "not a directory". Nothing propagates. * * @public */ interface SyncFileSystem { /** Whether a file or directory exists at `path`. Must not throw. */ readonly exists: (path: string) => boolean; /** Read the file at `path` as text. May throw; a throw degrades to a skip. */ readonly readFile: (path: string) => string; /** The entry names inside the directory at `path`. May throw; a throw skips the directory. */ readonly readDirectory: (path: string) => ReadonlyArray; /** Whether `path` is a directory. May throw; a throw reads as `false`. */ readonly isDirectory: (path: string) => boolean; /** * Optional fast path: the entries inside `path` with their types already * known, in ONE call. * * @remarks * Package enumeration otherwise costs a `readDirectory` plus one * `isDirectory` per entry — the readdir-then-stat-per-entry shape, which on * a large workspace is a syscall per file. Supplying this collapses that to * a single `readdirSync(path, { withFileTypes: true })`; `nodeFileSystem` * does. Omit it and enumeration falls back to the four required operations * with identical results, so this is purely a cost optimization and never a * behavior switch. * * May throw; a throw skips the directory, exactly like `readDirectory`. */ readonly readDirectoryWithTypes?: ((path: string) => ReadonlyArray) | undefined; } /** * One directory entry with its type resolved, as the optional * {@link SyncFileSystem.readDirectoryWithTypes} fast path reports it. Node's * `Dirent` satisfies it after mapping its predicate methods to booleans. * * @remarks * `isSymbolicLink` is not decoration. A `Dirent` describes the entry ITSELF, so * a symbolic link pointing at a directory reports `isDirectory: false` — while * the `stat`-based slow path, which resolves the link, calls the same entry a * directory. Enumeration therefore re-resolves links through `isDirectory` * rather than trusting `isDirectory` on a link, which is what keeps the fast * and slow paths in agreement on a workspace whose packages are symlinked. * * Which behavior is *correct* is domain-dependent, so the flag is reported * rather than resolved away. Enumeration follows links because a symlinked * package is still a package. A test-file discovery walk usually must NOT: in a * pnpm workspace `node_modules` is a farm of links into the content-addressed * store, and following them walks the whole store or hits a cycle. A consumer * building its own walker on this shape wants `isDirectory` verbatim — there, * not following is the requirement, not the hazard. * * @public */ interface SyncDirectoryEntry { /** The entry's own name, not a path. */ readonly name: string; /** Whether the entry itself is a directory. `false` for a symbolic link, even one targeting a directory. */ readonly isDirectory: boolean; /** Whether the entry itself is a symbolic link. */ readonly isSymbolicLink: boolean; } /** * The synchronous path operations the sync entry points need, supplied by the * consumer. Deliberately a structural subset of `node:path`, so the built-in * module (and its `win32` / `posix` variants, or a Bun / Deno equivalent) * satisfies it verbatim: * * ```ts * import * as path from "node:path"; * * const options: WorkspacesSyncOptions = { * fileSystem: { exists: existsSync, readFile: (p) => readFileSync(p, "utf8"), readDirectory: (p) => readdirSync(p), isDirectory: (p) => statSync(p).isDirectory() }, * path, // node:path IS a SyncPath * }; * ``` * * These operations shape only the ABSOLUTE paths handed back to the consumer * (and to its own `fileSystem`); workspace-relative pattern matching is POSIX * by the `packages:` contract and never routes through here. Windows * correctness comes from supplying a win32-appropriate implementation, not * from anything in this module. * * @public */ interface SyncPath { /** Join segments with the implementation's separator (like `path.join`). */ readonly join: (...segments: ReadonlyArray) => string; /** The directory portion of `p` (like `path.dirname`). */ readonly dirname: (p: string) => string; /** Resolve segments to an absolute path (rightmost-wins, like `path.resolve`). */ readonly resolve: (...segments: ReadonlyArray) => string; } /** * The consumer-supplied operations backing one sync call: the file operations * and the path implementation. Both are required — this package never imports * `node:*` and never assumes posix, so the platform binding is entirely the * caller's. * * @public */ interface WorkspacesSyncOptions { /** The synchronous file operations (Node: `existsSync` / `readFileSync` / `readdirSync` / `statSync`). */ readonly fileSystem: SyncFileSystem; /** The synchronous path implementation (Node: the `node:path` module itself). */ readonly path: SyncPath; } //#endregion //#region src/node-sync.d.ts /** * `SyncFileSystem` over `node:fs`. * * @remarks * `existsSync` never throws, satisfying `exists`'s must-not-throw contract; * the other three may throw and every throw lands in the sync entry points' * documented degraded-skip semantics. * * @public */ declare const nodeFileSystem: SyncFileSystem; /** * `SyncPath` as the running platform's `node:path` — win32 semantics on * Windows, posix elsewhere. Pass `node:path/win32` or `node:path/posix` * yourself to pin a dialect. * * @public */ declare const nodePath: SyncPath; /** * The complete Node-bound options bag for `findWorkspaceRootSync` and * `getWorkspacePackagesSync` — {@link nodeFileSystem} plus {@link nodePath}. * Both helpers take their path positionally, so this bag usually passes * through verbatim; spread it only to add `getWorkspacePackagesSync`'s * traversal extras: `{ ...nodeSyncOps, maxDepth }`. * * @public */ declare const nodeSyncOps: WorkspacesSyncOptions; //#endregion export { type SyncDirectoryEntry, type SyncFileSystem, type SyncPath, type WorkspacesSyncOptions, nodeFileSystem, nodePath, nodeSyncOps }; //# sourceMappingURL=node-sync.d.ts.map