( root: string, relPath: string, )
| 523 | } |
| 524 | |
| 525 | export async function readWorkspaceFileAt( |
| 526 | root: string, |
| 527 | relPath: string, |
| 528 | ): Promise<WorkspaceFileReadResult> { |
| 529 | const abs = await resolveSafeWorkspaceChildPath(root, relPath); |
| 530 | const rel = normalizeSlashes(relative(resolve(root), abs)); |
| 531 | assertWorkspacePathVisible(rel); |
| 532 | let size = 0; |
| 533 | let mtime = new Date(); |
| 534 | try { |
| 535 | const s = await stat(abs); |
| 536 | if (!s.isFile()) throw new Error(`not a file: ${relPath}`); |
| 537 | size = s.size; |
| 538 | mtime = s.mtime; |
| 539 | } catch (err) { |
| 540 | throw new Error(`stat failed: ${err instanceof Error ? err.message : String(err)}`); |
| 541 | } |
| 542 | if (size > MAX_SINGLE_FILE_BYTES) { |
| 543 | throw new Error(`file too large: ${relPath} (${size} bytes)`); |
| 544 | } |
| 545 | const kind = classifyWorkspaceFileKind(rel); |
| 546 | if (!isWorkspaceTextReadablePath(rel)) { |
| 547 | throw new Error(`not a text-readable workspace file: ${relPath}`); |
| 548 | } |
| 549 | |
| 550 | let content: string; |
| 551 | try { |
| 552 | content = await readUtf8TextFile(abs); |
| 553 | } catch (err) { |
| 554 | throw new Error(`read failed: ${err instanceof Error ? err.message : String(err)}`); |
| 555 | } |
| 556 | return { |
| 557 | path: rel, |
| 558 | kind, |
| 559 | size, |
| 560 | updatedAt: mtime.toISOString(), |
| 561 | content, |
| 562 | }; |
| 563 | } |
no test coverage detected