History

refactor workspace reads to Effect FileSystem

Cursor Agent committed 3 months ago

Commit 217fbf11 parent ebe8afb2 files · +138 −118Browse 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+87−89
@@ -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,
@@ -19,6 +16,7 @@ import * as Context from "effect/Context"; · 4 unmodified lines
1916 import * as Effect from "effect/Effect";
2017 import * as FileSystem from "effect/FileSystem";
2118 import * as Layer from "effect/Layer";
19+import * as Option from "effect/Option";
2220 import * as Path from "effect/Path";
2321 import * as Schema from "effect/Schema";
2422
@@ -140,30 +138,32 @@ export const make = Effect.gen(function* () { · 115 unmodified lines
140138 relativePath: input.relativePath,
141139 });
142140
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− });
141+ const realWorkspaceRoot = yield* fileSystem.realPath(input.cwd).pipe(
142+ Effect.mapError(
143+ (cause) =>
144+ new WorkspaceFileSystemOperationError({
145+ workspaceRoot: input.cwd,
146+ relativePath: input.relativePath,
147+ resolvedPath: target.absolutePath,
148+ operationPath: input.cwd,
149+ operation: "realpath-workspace-root",
150+ cause,
151+ }),
152+ ),
153+ );
154+ const realTargetPath = yield* fileSystem.realPath(target.absolutePath).pipe(
155+ Effect.mapError(
156+ (cause) =>
157+ new WorkspaceFileSystemOperationError({
158+ workspaceRoot: input.cwd,
159+ relativePath: input.relativePath,
160+ resolvedPath: target.absolutePath,
161+ operationPath: target.absolutePath,
162+ operation: "realpath-target",
163+ cause,
164+ }),
165+ ),
166+ );
167167 const relativeRealPath = path.relative(realWorkspaceRoot, realTargetPath);
168168 if (
169169 relativeRealPath.startsWith(`..${path.sep}`) ||
@@ -178,84 +178,82 @@ export const make = Effect.gen(function* () { · 8 unmodified lines
178178 });
179179 }
180180
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) =>
181+ return yield* Effect.scoped(
182+ Effect.gen(function* () {
183+ const handle = yield* fileSystem.open(realTargetPath, { flag: "r" }).pipe(
184+ Effect.mapError(
185+ (cause) =>
199186 new WorkspaceFileSystemOperationError({
200187 workspaceRoot: input.cwd,
201188 relativePath: input.relativePath,
202189 resolvedPath: realTargetPath,
203190 operationPath: realTargetPath,
204− operation: "stat",
191+ operation: "open",
205192 cause,
206193 }),
207− });
208− if (!stat.isFile()) {
209− return yield* new WorkspacePathNotFileError({
210− workspaceRoot: input.cwd,
211− relativePath: input.relativePath,
212− resolvedPath: realTargetPath,
213− });
214− }
215−
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) =>
194+ ),
195+ );
196+ const stat = yield* handle.stat.pipe(
197+ Effect.mapError(
198+ (cause) =>
221199 new WorkspaceFileSystemOperationError({
222200 workspaceRoot: input.cwd,
223201 relativePath: input.relativePath,
224202 resolvedPath: realTargetPath,
225203 operationPath: realTargetPath,
226− operation: "read",
204+ operation: "stat",
227205 cause,
228206 }),
207+ ),
208+ );
209+ if (stat.type !== "File") {
210+ return yield* new WorkspacePathNotFileError({
211+ workspaceRoot: input.cwd,
212+ relativePath: input.relativePath,
213+ resolvedPath: realTargetPath,
229214 });
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− }
215+ }
238216
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− }),
217+ const truncated = stat.size > FileSystem.Size(PROJECT_READ_FILE_MAX_BYTES);
218+ const byteLength = Number(stat.size);
219+ const bytesToRead = truncated ? PROJECT_READ_FILE_MAX_BYTES : byteLength;
220+ const fileBytes =
221+ bytesToRead === 0
222+ ? new Uint8Array()
223+ : yield* handle.readAlloc(bytesToRead).pipe(
224+ Effect.map(
225+ Option.match({
226+ onNone: () => new Uint8Array(),
227+ onSome: (bytes) => bytes,
228+ }),
229+ ),
230+ Effect.mapError(
231+ (cause) =>
232+ new WorkspaceFileSystemOperationError({
233+ workspaceRoot: input.cwd,
234+ relativePath: input.relativePath,
235+ resolvedPath: realTargetPath,
236+ operationPath: realTargetPath,
237+ operation: "read",
238+ cause,
239+ }),
240+ ),
241+ );
242+ if (fileBytes.includes(0)) {
243+ return yield* new WorkspaceBinaryFileError({
244+ workspaceRoot: input.cwd,
245+ relativePath: input.relativePath,
246+ resolvedPath: realTargetPath,
247+ });
248+ }
249+
250+ return {
251+ relativePath: target.relativePath,
252+ contents: new TextDecoder("utf-8").decode(fileBytes),
253+ byteLength,
254+ truncated,
255+ };
256+ }),
259257 );
260258 });
261259