History

refactor(server): use Effect filesystem for workspace reads

Cursor Agent committed 3 months ago

Commit 7c8c0b61 parent ecb35f72 files · +108 −91Browse 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+74−85
@@ -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,
@@ -140,30 +137,32 @@ export const make = Effect.gen(function* () { · 125 unmodified lines
140137 relativePath: input.relativePath,
141138 });
142139
143− const realWorkspaceRoot = yield* Effect.tryPromise({
144− try: () => NodeFSP.realpath(input.cwd),
145− catch: (cause) =>
146− new WorkspaceFileSystemOperationError({
147− workspaceRoot: input.cwd,
148− relativePath: input.relativePath,
149− resolvedPath: target.absolutePath,
150− operationPath: input.cwd,
151− operation: "realpath-workspace-root",
152− cause,
153− }),
154− });
155− const realTargetPath = yield* Effect.tryPromise({
156− try: () => NodeFSP.realpath(target.absolutePath),
157− catch: (cause) =>
158− new WorkspaceFileSystemOperationError({
159− workspaceRoot: input.cwd,
160− relativePath: input.relativePath,
161− resolvedPath: target.absolutePath,
162− operationPath: target.absolutePath,
163− operation: "realpath-target",
164− cause,
165− }),
166− });
140+ const realWorkspaceRoot = yield* fileSystem.realPath(input.cwd).pipe(
141+ Effect.mapError(
142+ (cause) =>
143+ new WorkspaceFileSystemOperationError({
144+ workspaceRoot: input.cwd,
145+ relativePath: input.relativePath,
146+ resolvedPath: target.absolutePath,
147+ operationPath: input.cwd,
148+ operation: "realpath-workspace-root",
149+ cause,
150+ }),
151+ ),
152+ );
153+ const realTargetPath = yield* fileSystem.realPath(target.absolutePath).pipe(
154+ Effect.mapError(
155+ (cause) =>
156+ new WorkspaceFileSystemOperationError({
157+ workspaceRoot: input.cwd,
158+ relativePath: input.relativePath,
159+ resolvedPath: target.absolutePath,
160+ operationPath: target.absolutePath,
161+ operation: "realpath-target",
162+ cause,
163+ }),
164+ ),
165+ );
167166 const relativeRealPath = path.relative(realWorkspaceRoot, realTargetPath);
168167 if (
169168 relativeRealPath.startsWith(`..${path.sep}`) ||
@@ -178,24 +177,24 @@ export const make = Effect.gen(function* () { · 8 unmodified lines
178177 });
179178 }
180179
181− return yield* Effect.acquireUseRelease(
182− Effect.tryPromise({
183− try: () => NodeFSP.open(realTargetPath, "r"),
184− catch: (cause) =>
185− new WorkspaceFileSystemOperationError({
186− workspaceRoot: input.cwd,
187− relativePath: input.relativePath,
188− resolvedPath: realTargetPath,
189− operationPath: realTargetPath,
190− operation: "open",
191− cause,
192− }),
193− }),
194− (handle) =>
195− Effect.gen(function* () {
196− const stat = yield* Effect.tryPromise({
197− try: () => handle.stat(),
198− catch: (cause) =>
180+ return yield* Effect.scoped(
181+ Effect.gen(function* () {
182+ const file = yield* fileSystem.open(realTargetPath, { flag: "r" }).pipe(
183+ Effect.mapError(
184+ (cause) =>
185+ new WorkspaceFileSystemOperationError({
186+ workspaceRoot: input.cwd,
187+ relativePath: input.relativePath,
188+ resolvedPath: realTargetPath,
189+ operationPath: realTargetPath,
190+ operation: "open",
191+ cause,
192+ }),
193+ ),
194+ );
195+ const stat = yield* file.stat.pipe(
196+ Effect.mapError(
197+ (cause) =>
199198 new WorkspaceFileSystemOperationError({
200199 workspaceRoot: input.cwd,
201200 relativePath: input.relativePath,
@@ -204,20 +203,22 @@ export const make = Effect.gen(function* () { · 2 unmodified lines
204203 operation: "stat",
205204 cause,
206205 }),
206+ ),
207+ );
208+ if (stat.type !== "File") {
209+ return yield* new WorkspacePathNotFileError({
210+ workspaceRoot: input.cwd,
211+ relativePath: input.relativePath,
212+ resolvedPath: realTargetPath,
207213 });
208− if (!stat.isFile()) {
209− return yield* new WorkspacePathNotFileError({
210− workspaceRoot: input.cwd,
211− relativePath: input.relativePath,
212− resolvedPath: realTargetPath,
213− });
214− }
214+ }
215215
216− const bytesToRead = Math.min(stat.size, PROJECT_READ_FILE_MAX_BYTES);
217− const buffer = Buffer.alloc(bytesToRead);
218− const { bytesRead } = yield* Effect.tryPromise({
219− try: () => handle.read(buffer, 0, bytesToRead, 0),
220− catch: (cause) =>
216+ const byteLength = Number(stat.size);
217+ const bytesToRead = Math.min(byteLength, PROJECT_READ_FILE_MAX_BYTES);
218+ const buffer = new Uint8Array(bytesToRead);
219+ const bytesRead = yield* file.read(buffer).pipe(
220+ Effect.mapError(
221+ (cause) =>
221222 new WorkspaceFileSystemOperationError({
222223 workspaceRoot: input.cwd,
223224 relativePath: input.relativePath,
@@ -226,36 +227,24 @@ export const make = Effect.gen(function* () { · 2 unmodified lines
226227 operation: "read",
227228 cause,
228229 }),
230+ ),
231+ );
232+ const fileBytes = buffer.subarray(0, Number(bytesRead));
233+ if (fileBytes.includes(0)) {
234+ return yield* new WorkspaceBinaryFileError({
235+ workspaceRoot: input.cwd,
236+ relativePath: input.relativePath,
237+ resolvedPath: realTargetPath,
229238 });
230− const fileBytes = buffer.subarray(0, bytesRead);
231− if (fileBytes.includes(0)) {
232− return yield* new WorkspaceBinaryFileError({
233− workspaceRoot: input.cwd,
234− relativePath: input.relativePath,
235− resolvedPath: realTargetPath,
236− });
237− }
239+ }
238240
239− return {
240− relativePath: target.relativePath,
241− contents: new TextDecoder("utf-8").decode(fileBytes),
242− byteLength: stat.size,
243− truncated: stat.size > PROJECT_READ_FILE_MAX_BYTES,
244− };
245− }),
246− (handle) =>
247− Effect.tryPromise({
248− try: () => handle.close(),
249− catch: (cause) =>
250− new WorkspaceFileSystemOperationError({
251− workspaceRoot: input.cwd,
252− relativePath: input.relativePath,
253− resolvedPath: realTargetPath,
254− operationPath: realTargetPath,
255− operation: "close",
256− cause,
257− }),
258− }),
241+ return {
242+ relativePath: target.relativePath,
243+ contents: new TextDecoder("utf-8").decode(fileBytes),
244+ byteLength,
245+ truncated: byteLength > PROJECT_READ_FILE_MAX_BYTES,
246+ };
247+ }),
259248 );
260249 });
261250