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
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,
@@ -140,30 +137,32 @@ export const make = Effect.gen(function* () { · 125 unmodified lines
140 relativePath: input.relativePath,137 relativePath: input.relativePath,
141 });138 });
142 139
143 const realWorkspaceRoot = yield* Effect.tryPromise({140 const realWorkspaceRoot = yield* fileSystem.realPath(input.cwd).pipe(
144 try: () => NodeFSP.realpath(input.cwd),141 Effect.mapError(
145 catch: (cause) =>142 (cause) =>
146 new WorkspaceFileSystemOperationError({143 new WorkspaceFileSystemOperationError({
147 workspaceRoot: input.cwd,144 workspaceRoot: input.cwd,
148 relativePath: input.relativePath,145 relativePath: input.relativePath,
149 resolvedPath: target.absolutePath,146 resolvedPath: target.absolutePath,
150 operationPath: input.cwd,147 operationPath: input.cwd,
151 operation: "realpath-workspace-root",148 operation: "realpath-workspace-root",
152 cause,149 cause,
153 }),150 }),
154 });151 ),
155 const realTargetPath = yield* Effect.tryPromise({152 );
156 try: () => NodeFSP.realpath(target.absolutePath),153 const realTargetPath = yield* fileSystem.realPath(target.absolutePath).pipe(
157 catch: (cause) =>154 Effect.mapError(
158 new WorkspaceFileSystemOperationError({155 (cause) =>
159 workspaceRoot: input.cwd,156 new WorkspaceFileSystemOperationError({
160 relativePath: input.relativePath,157 workspaceRoot: input.cwd,
161 resolvedPath: target.absolutePath,158 relativePath: input.relativePath,
162 operationPath: target.absolutePath,159 resolvedPath: target.absolutePath,
163 operation: "realpath-target",160 operationPath: target.absolutePath,
164 cause,161 operation: "realpath-target",
165 }),162 cause,
166 });163 }),
164 ),
165 );
167 const relativeRealPath = path.relative(realWorkspaceRoot, realTargetPath);166 const relativeRealPath = path.relative(realWorkspaceRoot, realTargetPath);
168 if (167 if (
169 relativeRealPath.startsWith(`..${path.sep}`) ||168 relativeRealPath.startsWith(`..${path.sep}`) ||
@@ -178,24 +177,24 @@ export const make = Effect.gen(function* () { · 8 unmodified lines
178 });177 });
179 }178 }
180 179
181 return yield* Effect.acquireUseRelease(180 return yield* Effect.scoped(
182 Effect.tryPromise({181 Effect.gen(function* () {
183 try: () => NodeFSP.open(realTargetPath, "r"),182 const file = yield* fileSystem.open(realTargetPath, { flag: "r" }).pipe(
184 catch: (cause) =>183 Effect.mapError(
185 new WorkspaceFileSystemOperationError({184 (cause) =>
186 workspaceRoot: input.cwd,185 new WorkspaceFileSystemOperationError({
187 relativePath: input.relativePath,186 workspaceRoot: input.cwd,
188 resolvedPath: realTargetPath,187 relativePath: input.relativePath,
189 operationPath: realTargetPath,188 resolvedPath: realTargetPath,
190 operation: "open",189 operationPath: realTargetPath,
191 cause,190 operation: "open",
192 }),191 cause,
193 }),192 }),
194 (handle) =>193 ),
195 Effect.gen(function* () {194 );
196 const stat = yield* Effect.tryPromise({195 const stat = yield* file.stat.pipe(
197 try: () => handle.stat(),196 Effect.mapError(
198 catch: (cause) =>197 (cause) =>
199 new WorkspaceFileSystemOperationError({198 new WorkspaceFileSystemOperationError({
200 workspaceRoot: input.cwd,199 workspaceRoot: input.cwd,
201 relativePath: input.relativePath,200 relativePath: input.relativePath,
@@ -204,20 +203,22 @@ export const make = Effect.gen(function* () { · 2 unmodified lines
204 operation: "stat",203 operation: "stat",
205 cause,204 cause,
206 }),205 }),
206 ),
207 );
208 if (stat.type !== "File") {
209 return yield* new WorkspacePathNotFileError({
210 workspaceRoot: input.cwd,
211 relativePath: input.relativePath,
212 resolvedPath: realTargetPath,
207 });213 });
208 if (!stat.isFile()) {214 }
209 return yield* new WorkspacePathNotFileError({
210 workspaceRoot: input.cwd,
211 relativePath: input.relativePath,
212 resolvedPath: realTargetPath,
213 });
214 }
215 215
216 const bytesToRead = Math.min(stat.size, PROJECT_READ_FILE_MAX_BYTES);216 const byteLength = Number(stat.size);
217 const buffer = Buffer.alloc(bytesToRead);217 const bytesToRead = Math.min(byteLength, PROJECT_READ_FILE_MAX_BYTES);
218 const { bytesRead } = yield* Effect.tryPromise({218 const buffer = new Uint8Array(bytesToRead);
219 try: () => handle.read(buffer, 0, bytesToRead, 0),219 const bytesRead = yield* file.read(buffer).pipe(
220 catch: (cause) =>220 Effect.mapError(
221 (cause) =>
221 new WorkspaceFileSystemOperationError({222 new WorkspaceFileSystemOperationError({
222 workspaceRoot: input.cwd,223 workspaceRoot: input.cwd,
223 relativePath: input.relativePath,224 relativePath: input.relativePath,
@@ -226,36 +227,24 @@ export const make = Effect.gen(function* () { · 2 unmodified lines
226 operation: "read",227 operation: "read",
227 cause,228 cause,
228 }),229 }),
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,
229 });238 });
230 const fileBytes = buffer.subarray(0, bytesRead);239 }
231 if (fileBytes.includes(0)) {
232 return yield* new WorkspaceBinaryFileError({
233 workspaceRoot: input.cwd,
234 relativePath: input.relativePath,
235 resolvedPath: realTargetPath,
236 });
237 }
238 240
239 return {241 return {
240 relativePath: target.relativePath,242 relativePath: target.relativePath,
241 contents: new TextDecoder("utf-8").decode(fileBytes),243 contents: new TextDecoder("utf-8").decode(fileBytes),
242 byteLength: stat.size,244 byteLength,
243 truncated: stat.size > PROJECT_READ_FILE_MAX_BYTES,245 truncated: byteLength > PROJECT_READ_FILE_MAX_BYTES,
244 };246 };
245 }),247 }),
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 }),
259 );248 );
260 });249 });
261 250