History

Use Effect FileSystem for workspace reads

Cursor Agent committed 4 months ago

Commit 05760161 parent 97e5cd32 files · +115 −59Browse files

Showing the changes to apps/server/src/workspace/WorkspaceFileSystem.ts (1 of 2 files). Show all

Unified · Split

apps/server/src/workspace/WorkspaceFileSystem.ts+39−40
@@ -1,4 +1,3 @@
1−// @effect-diagnostics nodeBuiltinImport:off
21 /**
32 * WorkspaceFileSystem - Effect service contract for workspace file mutations.
43 *
@@ -7,8 +6,6 @@ · 2 unmodified lines
76 *
87 * @module WorkspaceFileSystem
98 */
10−import * as NodeFSP from "node:fs/promises";
11−
129 import type {
1310 ProjectReadFileInput,
1411 ProjectReadFileResult,
@@ -86,11 +83,11 @@ export const make = Effect.gen(function* () { · 71 unmodified lines
8683 relativePath: input.relativePath,
8784 });
8885
89− return yield* Effect.tryPromise({
90− try: async () => {
91− const [realWorkspaceRoot, realTargetPath] = await Promise.all([
92− NodeFSP.realpath(input.cwd),
93− NodeFSP.realpath(target.absolutePath),
86+ return yield* Effect.scoped(
87+ Effect.gen(function* () {
88+ const [realWorkspaceRoot, realTargetPath] = yield* Effect.all([
89+ fileSystem.realPath(input.cwd),
90+ fileSystem.realPath(target.absolutePath),
9491 ]);
9592 const relativeRealPath = path.relative(realWorkspaceRoot, realTargetPath);
9693 if (
@@ -98,41 +95,43 @@ export const make = Effect.gen(function* () { · 1 unmodified line
9895 relativeRealPath === ".." ||
9996 path.isAbsolute(relativeRealPath)
10097 ) {
101− throw new Error("Workspace file path resolves outside the project root.");
98+ return yield* Effect.fail(
99+ new Error("Workspace file path resolves outside the project root."),
100+ );
102101 }
103102
104− const handle = await NodeFSP.open(realTargetPath, "r");
105− try {
106− const stat = await handle.stat();
107− if (!stat.isFile()) {
108− throw new Error("Workspace path is not a file.");
109− }
110− const bytesToRead = Math.min(stat.size, PROJECT_READ_FILE_MAX_BYTES);
111− const buffer = Buffer.alloc(bytesToRead);
112− const { bytesRead } = await handle.read(buffer, 0, bytesToRead, 0);
113− const fileBytes = buffer.subarray(0, bytesRead);
114− if (fileBytes.includes(0)) {
115− throw new Error("Binary files cannot be previewed as text.");
116− }
117− const contents = new TextDecoder("utf-8").decode(fileBytes);
118− return {
119− relativePath: target.relativePath,
120− contents,
121− byteLength: stat.size,
122− truncated: stat.size > PROJECT_READ_FILE_MAX_BYTES,
123− };
124− } finally {
125− await handle.close();
103+ const handle = yield* fileSystem.open(realTargetPath, { flag: "r" });
104+ const stat = yield* handle.stat;
105+ if (stat.type !== "File") {
106+ return yield* Effect.fail(new Error("Workspace path is not a file."));
126107 }
127− },
128− catch: (cause) =>
129− new WorkspaceFileSystemError({
130− cwd: input.cwd,
131− relativePath: input.relativePath,
132− operation: "workspaceFileSystem.readFile",
133− cause,
134− }),
135− });
108+ const byteLength = Number(stat.size);
109+ const bytesToRead = Math.min(byteLength, PROJECT_READ_FILE_MAX_BYTES);
110+ const buffer = new Uint8Array(bytesToRead);
111+ const bytesRead = yield* handle.read(buffer);
112+ const fileBytes = buffer.subarray(0, Number(bytesRead));
113+ if (fileBytes.includes(0)) {
114+ return yield* Effect.fail(new Error("Binary files cannot be previewed as text."));
115+ }
116+ const contents = new TextDecoder("utf-8").decode(fileBytes);
117+ return {
118+ relativePath: target.relativePath,
119+ contents,
120+ byteLength,
121+ truncated: stat.size > BigInt(PROJECT_READ_FILE_MAX_BYTES),
122+ };
123+ }),
124+ ).pipe(
125+ Effect.mapError(
126+ (cause) =>
127+ new WorkspaceFileSystemError({
128+ cwd: input.cwd,
129+ relativePath: input.relativePath,
130+ operation: "workspaceFileSystem.readFile",
131+ cause,
132+ }),
133+ ),
134+ );
136135 });
137136
138137 const writeFile: WorkspaceFileSystem["Service"]["writeFile"] = Effect.fn(