History

refactor workspace reads to Effect FileSystem

Cursor Agent committed 3 months ago

Commit 51f34031 parent fdca1542 files · +122 −113Browse 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−84
@@ -1,4 +1,3 @@
1−// @effect-diagnostics nodeBuiltinImport:off
21 /**
32 * WorkspaceFileSystem - Effect service contract for workspace file mutations.
43 *
@@ -7,7 +6,6 @@ · 2 unmodified lines
76 *
87 * @module WorkspaceFileSystem
98 */
10−import * as NodeFSP from "node:fs/promises";
119
1210 import type {
1311 ProjectReadFileInput,
@@ -19,6 +17,7 @@ import * as Context from "effect/Context"; · 5 unmodified lines
1917 import * as Effect from "effect/Effect";
2018 import * as FileSystem from "effect/FileSystem";
2119 import * as Layer from "effect/Layer";
20+import * as Option from "effect/Option";
2221 import * as Path from "effect/Path";
2322 import * as Schema from "effect/Schema";
2423
@@ -140,30 +139,32 @@ export const make = Effect.gen(function* () { · 115 unmodified lines
140139 relativePath: input.relativePath,
141140 });
142141
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− });
142+ const realWorkspaceRoot = yield* fileSystem.realPath(input.cwd).pipe(
143+ Effect.mapError(
144+ (cause) =>
145+ new WorkspaceFileSystemOperationError({
146+ workspaceRoot: input.cwd,
147+ relativePath: input.relativePath,
148+ resolvedPath: target.absolutePath,
149+ operationPath: input.cwd,
150+ operation: "realpath-workspace-root",
151+ cause,
152+ }),
153+ ),
154+ );
155+ const realTargetPath = yield* fileSystem.realPath(target.absolutePath).pipe(
156+ Effect.mapError(
157+ (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+ ),
167+ );
167168 const relativeRealPath = path.relative(realWorkspaceRoot, realTargetPath);
168169 if (
169170 relativeRealPath.startsWith(`..${path.sep}`) ||
@@ -178,24 +179,24 @@ export const make = Effect.gen(function* () { · 8 unmodified lines
178179 });
179180 }
180181
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) =>
182+ return yield* Effect.scoped(
183+ Effect.gen(function* () {
184+ const handle = yield* fileSystem.open(realTargetPath, { flag: "r" }).pipe(
185+ Effect.mapError(
186+ (cause) =>
187+ new WorkspaceFileSystemOperationError({
188+ workspaceRoot: input.cwd,
189+ relativePath: input.relativePath,
190+ resolvedPath: realTargetPath,
191+ operationPath: realTargetPath,
192+ operation: "open",
193+ cause,
194+ }),
195+ ),
196+ );
197+ const stat = yield* handle.stat.pipe(
198+ Effect.mapError(
199+ (cause) =>
199200 new WorkspaceFileSystemOperationError({
200201 workspaceRoot: input.cwd,
201202 relativePath: input.relativePath,
@@ -204,20 +205,21 @@ export const make = Effect.gen(function* () { · 2 unmodified lines
204205 operation: "stat",
205206 cause,
206207 }),
208+ ),
209+ );
210+ if (stat.type !== "File") {
211+ return yield* new WorkspacePathNotFileError({
212+ workspaceRoot: input.cwd,
213+ relativePath: input.relativePath,
214+ resolvedPath: realTargetPath,
207215 });
208− if (!stat.isFile()) {
209− return yield* new WorkspacePathNotFileError({
210− workspaceRoot: input.cwd,
211− relativePath: input.relativePath,
212− resolvedPath: realTargetPath,
213− });
214− }
216+ }
215217
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) =>
218+ const byteLength = Number(stat.size);
219+ const bytesToRead = Math.min(byteLength, PROJECT_READ_FILE_MAX_BYTES);
220+ const maybeFileBytes = yield* handle.readAlloc(bytesToRead).pipe(
221+ Effect.mapError(
222+ (cause) =>
221223 new WorkspaceFileSystemOperationError({
222224 workspaceRoot: input.cwd,
223225 relativePath: input.relativePath,
@@ -226,36 +228,24 @@ export const make = Effect.gen(function* () { · 2 unmodified lines
226228 operation: "read",
227229 cause,
228230 }),
231+ ),
232+ );
233+ const fileBytes = Option.getOrElse(maybeFileBytes, () => new Uint8Array());
234+ if (fileBytes.includes(0)) {
235+ return yield* new WorkspaceBinaryFileError({
236+ workspaceRoot: input.cwd,
237+ relativePath: input.relativePath,
238+ resolvedPath: realTargetPath,
229239 });
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− }
240+ }
238241
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− }),
242+ return {
243+ relativePath: target.relativePath,
244+ contents: new TextDecoder("utf-8").decode(fileBytes),
245+ byteLength,
246+ truncated: byteLength > PROJECT_READ_FILE_MAX_BYTES,
247+ };
248+ }),
259249 );
260250 });
261251