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
2/**1/**
3 * WorkspaceFileSystem - Effect service contract for workspace file mutations.2 * WorkspaceFileSystem - Effect service contract for workspace file mutations.
4 *3 *
@@ -7,8 +6,6 @@ · 2 unmodified lines
7 *6 *
8 * @module WorkspaceFileSystem7 * @module WorkspaceFileSystem
9 */8 */
10import * as NodeFSP from "node:fs/promises";
11
12import type {9import type {
13 ProjectReadFileInput,10 ProjectReadFileInput,
14 ProjectReadFileResult,11 ProjectReadFileResult,
@@ -86,11 +83,11 @@ export const make = Effect.gen(function* () { · 71 unmodified lines
86 relativePath: input.relativePath,83 relativePath: input.relativePath,
87 });84 });
88 85
89 return yield* Effect.tryPromise({86 return yield* Effect.scoped(
90 try: async () => {87 Effect.gen(function* () {
91 const [realWorkspaceRoot, realTargetPath] = await Promise.all([88 const [realWorkspaceRoot, realTargetPath] = yield* Effect.all([
92 NodeFSP.realpath(input.cwd),89 fileSystem.realPath(input.cwd),
93 NodeFSP.realpath(target.absolutePath),90 fileSystem.realPath(target.absolutePath),
94 ]);91 ]);
95 const relativeRealPath = path.relative(realWorkspaceRoot, realTargetPath);92 const relativeRealPath = path.relative(realWorkspaceRoot, realTargetPath);
96 if (93 if (
@@ -98,41 +95,43 @@ export const make = Effect.gen(function* () { · 1 unmodified line
98 relativeRealPath === ".." ||95 relativeRealPath === ".." ||
99 path.isAbsolute(relativeRealPath)96 path.isAbsolute(relativeRealPath)
100 ) {97 ) {
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 );
102 }101 }
103 102
104 const handle = await NodeFSP.open(realTargetPath, "r");103 const handle = yield* fileSystem.open(realTargetPath, { flag: "r" });
105 try {104 const stat = yield* handle.stat;
106 const stat = await handle.stat();105 if (stat.type !== "File") {
107 if (!stat.isFile()) {106 return yield* Effect.fail(new Error("Workspace path is not a file."));
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();
126 }107 }
127 },108 const byteLength = Number(stat.size);
128 catch: (cause) =>109 const bytesToRead = Math.min(byteLength, PROJECT_READ_FILE_MAX_BYTES);
129 new WorkspaceFileSystemError({110 const buffer = new Uint8Array(bytesToRead);
130 cwd: input.cwd,111 const bytesRead = yield* handle.read(buffer);
131 relativePath: input.relativePath,112 const fileBytes = buffer.subarray(0, Number(bytesRead));
132 operation: "workspaceFileSystem.readFile",113 if (fileBytes.includes(0)) {
133 cause,114 return yield* Effect.fail(new Error("Binary files cannot be previewed as text."));
134 }),115 }
135 });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 );
136 });135 });
137 136
138 const writeFile: WorkspaceFileSystem["Service"]["writeFile"] = Effect.fn(137 const writeFile: WorkspaceFileSystem["Service"]["writeFile"] = Effect.fn(