import * as NodeServices from "@effect/platform-node/NodeServices";import { it, describe, expect } from "@effect/vitest";import * as Effect from "effect/Effect";import * as FileSystem from "effect/FileSystem";import * as Layer from "effect/Layer";import * as Path from "effect/Path";import * as PlatformError from "effect/PlatformError"; import * as WorkspacePaths from "./WorkspacePaths.ts"; const TestLayer = Layer.empty.pipe( Layer.provideMerge(WorkspacePaths.layer), Layer.provideMerge(NodeServices.layer),); const makeTempDir = Effect.fn("makeTempDir")(function* () { const fileSystem = yield* FileSystem.FileSystem; return yield* fileSystem.makeTempDirectoryScoped({ prefix: "t3code-project-paths-", });}); const writeTextFile = Effect.fn("writeTextFile")(function* ( cwd: string, relativePath: string, contents = "",) { const fileSystem = yield* FileSystem.FileSystem; const path = yield* Path.Path; const absolutePath = path.join(cwd, relativePath); yield* fileSystem .makeDirectory(path.dirname(absolutePath), { recursive: true }) .pipe(Effect.orDie); yield* fileSystem.writeFileString(absolutePath, contents).pipe(Effect.orDie);}); it.layer(TestLayer)("WorkspacePathsLive", (it) => { describe("normalizeWorkspaceRoot", () => { it.effect("resolves an existing directory", () => Effect.gen(function* () { const workspacePaths = yield* WorkspacePaths.WorkspacePaths; const cwd = yield* makeTempDir(); const resolved = yield* workspacePaths.normalizeWorkspaceRoot(cwd); expect(resolved).toBe(cwd); }), ); it.effect("rejects missing directories", () => Effect.gen(function* () { const workspacePaths = yield* WorkspacePaths.WorkspacePaths; const cwd = yield* makeTempDir(); const path = yield* Path.Path; const error = yield* workspacePaths .normalizeWorkspaceRoot(path.join(cwd, "missing")) .pipe(Effect.flip); expect(error.message).toContain("Workspace root does not exist:"); }), ); it.effect("creates missing directories when createIfMissing is enabled", () => Effect.gen(function* () { const workspacePaths = yield* WorkspacePaths.WorkspacePaths; const fileSystem = yield* FileSystem.FileSystem; const cwd = yield* makeTempDir(); const path = yield* Path.Path; const missingPath = path.join(cwd, "nested", "new-project"); const resolved = yield* workspacePaths.normalizeWorkspaceRoot(missingPath, { createIfMissing: true, }); const stat = yield* fileSystem.stat(resolved); expect(resolved).toBe(missingPath); expect(stat.type).toBe("Directory"); }), ); it.effect("rejects file paths", () => Effect.gen(function* () { const workspacePaths = yield* WorkspacePaths.WorkspacePaths; const cwd = yield* makeTempDir(); const path = yield* Path.Path; const filePath = path.join(cwd, "README.md"); yield* writeTextFile(cwd, "README.md", "# hi\n"); const error = yield* workspacePaths.normalizeWorkspaceRoot(filePath).pipe(Effect.flip); expect(error.message).toContain("Workspace root is not a directory:"); }), ); it.effect("preserves non-NotFound stat failures while validating the root", () => Effect.gen(function* () { const fileSystem = yield* FileSystem.FileSystem; const workspacePaths = yield* WorkspacePaths.make.pipe( Effect.provideService(FileSystem.FileSystem, { ...fileSystem, stat: (path) => Effect.fail( PlatformError.systemError({ _tag: "PermissionDenied", module: "FileSystem", method: "stat", pathOrDescriptor: String(path), description: "Test PermissionDenied stat failure.", }), ), }), ); const path = yield* Path.Path; const workspaceRoot = " ./permission-denied "; const normalizedWorkspaceRoot = path.resolve(workspaceRoot.trim()); const error = yield* workspacePaths.normalizeWorkspaceRoot(workspaceRoot).pipe(Effect.flip); expect(error).toBeInstanceOf(WorkspacePaths.WorkspaceRootStatFailedError); expect(error).toMatchObject({ workspaceRoot, normalizedWorkspaceRoot, phase: "validate-existing", }); }), ); it.effect("preserves stat failures while verifying a newly created root", () => Effect.gen(function* () { const fileSystem = yield* FileSystem.FileSystem; let statCalls = 0; const workspacePaths = yield* WorkspacePaths.make.pipe( Effect.provideService(FileSystem.FileSystem, { ...fileSystem, stat: (path) => { statCalls += 1; const reason = statCalls === 1 ? "NotFound" : "PermissionDenied"; return Effect.fail( PlatformError.systemError({ _tag: reason, module: "FileSystem", method: "stat", pathOrDescriptor: String(path), description: `Test ${reason} stat failure.`, }), ); }, makeDirectory: () => Effect.void, }), ); const path = yield* Path.Path; const workspaceRoot = " ./created-then-unreadable "; const normalizedWorkspaceRoot = path.resolve(workspaceRoot.trim()); const error = yield* workspacePaths .normalizeWorkspaceRoot(workspaceRoot, { createIfMissing: true }) .pipe(Effect.flip); expect(error).toBeInstanceOf(WorkspacePaths.WorkspaceRootStatFailedError); expect(error).toMatchObject({ workspaceRoot, normalizedWorkspaceRoot, phase: "verify-created", }); }), ); }); describe("resolveRelativePathWithinRoot", () => { it.effect("resolves relative paths inside the workspace root", () => Effect.gen(function* () { const workspacePaths = yield* WorkspacePaths.WorkspacePaths; const cwd = yield* makeTempDir(); const path = yield* Path.Path; const resolved = yield* workspacePaths.resolveRelativePathWithinRoot({ workspaceRoot: cwd, relativePath: "plans/effect-rpc.md", }); expect(resolved).toEqual({ absolutePath: path.join(cwd, "plans/effect-rpc.md"), relativePath: "plans/effect-rpc.md", }); }), ); it.effect("rejects paths that escape the workspace root", () => Effect.gen(function* () { const workspacePaths = yield* WorkspacePaths.WorkspacePaths; const cwd = yield* makeTempDir(); const error = yield* workspacePaths .resolveRelativePathWithinRoot({ workspaceRoot: cwd, relativePath: "../escape.md", }) .pipe(Effect.flip); expect(error.message).toContain( "Workspace file path must be relative to the project root: ../escape.md", ); }), ); });});
import * as NodeServices from "@effect/platform-node/NodeServices";
import { it, describe, expect } from "@effect/vitest";
import * as Effect from "effect/Effect";
import * as FileSystem from "effect/FileSystem";
import * as Layer from "effect/Layer";
import * as Path from "effect/Path";
import * as PlatformError from "effect/PlatformError";
import * as WorkspacePaths from "./WorkspacePaths.ts";
const TestLayer = Layer.empty.pipe(
Layer.provideMerge(WorkspacePaths.layer),
Layer.provideMerge(NodeServices.layer),
);
const makeTempDir = Effect.fn("makeTempDir")(function* () {
const fileSystem = yield* FileSystem.FileSystem;
return yield* fileSystem.makeTempDirectoryScoped({
prefix: "t3code-project-paths-",
});
});
const writeTextFile = Effect.fn("writeTextFile")(function* (
cwd: string,
relativePath: string,
contents = "",
) {
const fileSystem = yield* FileSystem.FileSystem;
const path = yield* Path.Path;
const absolutePath = path.join(cwd, relativePath);
yield* fileSystem
.makeDirectory(path.dirname(absolutePath), { recursive: true })
.pipe(Effect.orDie);
yield* fileSystem.writeFileString(absolutePath, contents).pipe(Effect.orDie);
});
it.layer(TestLayer)("WorkspacePathsLive", (it) => {
describe("normalizeWorkspaceRoot", () => {
it.effect("resolves an existing directory", () =>
Effect.gen(function* () {
const workspacePaths = yield* WorkspacePaths.WorkspacePaths;
const cwd = yield* makeTempDir();
const resolved = yield* workspacePaths.normalizeWorkspaceRoot(cwd);
expect(resolved).toBe(cwd);
}),
);
it.effect("rejects missing directories", () =>
Effect.gen(function* () {
const workspacePaths = yield* WorkspacePaths.WorkspacePaths;
const cwd = yield* makeTempDir();
const path = yield* Path.Path;
const error = yield* workspacePaths
.normalizeWorkspaceRoot(path.join(cwd, "missing"))
.pipe(Effect.flip);
expect(error.message).toContain("Workspace root does not exist:");
}),
);
it.effect("creates missing directories when createIfMissing is enabled", () =>
Effect.gen(function* () {
const workspacePaths = yield* WorkspacePaths.WorkspacePaths;
const fileSystem = yield* FileSystem.FileSystem;
const cwd = yield* makeTempDir();
const path = yield* Path.Path;
const missingPath = path.join(cwd, "nested", "new-project");
const resolved = yield* workspacePaths.normalizeWorkspaceRoot(missingPath, {
createIfMissing: true,
});
const stat = yield* fileSystem.stat(resolved);
expect(resolved).toBe(missingPath);
expect(stat.type).toBe("Directory");
}),
);
it.effect("rejects file paths", () =>
Effect.gen(function* () {
const workspacePaths = yield* WorkspacePaths.WorkspacePaths;
const cwd = yield* makeTempDir();
const path = yield* Path.Path;
const filePath = path.join(cwd, "README.md");
yield* writeTextFile(cwd, "README.md", "# hi\n");
const error = yield* workspacePaths.normalizeWorkspaceRoot(filePath).pipe(Effect.flip);
expect(error.message).toContain("Workspace root is not a directory:");
}),
);
it.effect("preserves non-NotFound stat failures while validating the root", () =>
Effect.gen(function* () {
const fileSystem = yield* FileSystem.FileSystem;
const workspacePaths = yield* WorkspacePaths.make.pipe(
Effect.provideService(FileSystem.FileSystem, {
...fileSystem,
stat: (path) =>
Effect.fail(
PlatformError.systemError({
_tag: "PermissionDenied",
module: "FileSystem",
method: "stat",
pathOrDescriptor: String(path),
description: "Test PermissionDenied stat failure.",
}),
),
}),
);
const path = yield* Path.Path;
const workspaceRoot = " ./permission-denied ";
const normalizedWorkspaceRoot = path.resolve(workspaceRoot.trim());
const error = yield* workspacePaths.normalizeWorkspaceRoot(workspaceRoot).pipe(Effect.flip);
expect(error).toBeInstanceOf(WorkspacePaths.WorkspaceRootStatFailedError);
expect(error).toMatchObject({
workspaceRoot,
normalizedWorkspaceRoot,
phase: "validate-existing",
});
}),
);
it.effect("preserves stat failures while verifying a newly created root", () =>
Effect.gen(function* () {
const fileSystem = yield* FileSystem.FileSystem;
let statCalls = 0;
const workspacePaths = yield* WorkspacePaths.make.pipe(
Effect.provideService(FileSystem.FileSystem, {
...fileSystem,
stat: (path) => {
statCalls += 1;
const reason = statCalls === 1 ? "NotFound" : "PermissionDenied";
return Effect.fail(
PlatformError.systemError({
_tag: reason,
module: "FileSystem",
method: "stat",
pathOrDescriptor: String(path),
description: `Test ${reason} stat failure.`,
}),
);
},
makeDirectory: () => Effect.void,
}),
);
const path = yield* Path.Path;
const workspaceRoot = " ./created-then-unreadable ";
const normalizedWorkspaceRoot = path.resolve(workspaceRoot.trim());
const error = yield* workspacePaths
.normalizeWorkspaceRoot(workspaceRoot, { createIfMissing: true })
.pipe(Effect.flip);
expect(error).toBeInstanceOf(WorkspacePaths.WorkspaceRootStatFailedError);
expect(error).toMatchObject({
workspaceRoot,
normalizedWorkspaceRoot,
phase: "verify-created",
});
}),
);
});
describe("resolveRelativePathWithinRoot", () => {
it.effect("resolves relative paths inside the workspace root", () =>
Effect.gen(function* () {
const workspacePaths = yield* WorkspacePaths.WorkspacePaths;
const cwd = yield* makeTempDir();
const path = yield* Path.Path;
const resolved = yield* workspacePaths.resolveRelativePathWithinRoot({
workspaceRoot: cwd,
relativePath: "plans/effect-rpc.md",
});
expect(resolved).toEqual({
absolutePath: path.join(cwd, "plans/effect-rpc.md"),
relativePath: "plans/effect-rpc.md",
});
}),
);
it.effect("rejects paths that escape the workspace root", () =>
Effect.gen(function* () {
const workspacePaths = yield* WorkspacePaths.WorkspacePaths;
const cwd = yield* makeTempDir();
const error = yield* workspacePaths
.resolveRelativePathWithinRoot({
workspaceRoot: cwd,
relativePath: "../escape.md",
})
.pipe(Effect.flip);
expect(error.message).toContain(
"Workspace file path must be relative to the project root: ../escape.md",
);
}),
);
});
});