@@ -17,6 +17,7 @@ import micromatch from "micromatch";
1717import type {
1818 BackendProtocol ,
1919 EditResult ,
20+ FileData ,
2021 FileInfo ,
2122 GrepMatch ,
2223 WriteResult ,
@@ -241,6 +242,46 @@ export class FilesystemBackend implements BackendProtocol {
241242 }
242243 }
243244
245+ /**
246+ * Read file content as raw FileData.
247+ *
248+ * @param filePath - Absolute file path
249+ * @returns Raw file content as FileData
250+ */
251+ async readRaw ( filePath : string ) : Promise < FileData > {
252+ const resolvedPath = this . resolvePath ( filePath ) ;
253+
254+ let content : string ;
255+ let stat : fsSync . Stats ;
256+
257+ if ( SUPPORTS_NOFOLLOW ) {
258+ stat = await fs . stat ( resolvedPath ) ;
259+ if ( ! stat . isFile ( ) ) throw new Error ( `File '${ filePath } ' not found` ) ;
260+ const fd = await fs . open (
261+ resolvedPath ,
262+ fsSync . constants . O_RDONLY | fsSync . constants . O_NOFOLLOW ,
263+ ) ;
264+ try {
265+ content = await fd . readFile ( { encoding : "utf-8" } ) ;
266+ } finally {
267+ await fd . close ( ) ;
268+ }
269+ } else {
270+ stat = await fs . lstat ( resolvedPath ) ;
271+ if ( stat . isSymbolicLink ( ) ) {
272+ throw new Error ( `Symlinks are not allowed: ${ filePath } ` ) ;
273+ }
274+ if ( ! stat . isFile ( ) ) throw new Error ( `File '${ filePath } ' not found` ) ;
275+ content = await fs . readFile ( resolvedPath , "utf-8" ) ;
276+ }
277+
278+ return {
279+ content : content . split ( "\n" ) ,
280+ created_at : stat . ctime . toISOString ( ) ,
281+ modified_at : stat . mtime . toISOString ( ) ,
282+ } ;
283+ }
284+
244285 /**
245286 * Create a new file with content.
246287 * Returns WriteResult. External storage sets filesUpdate=null.
0 commit comments