@@ -250,9 +250,7 @@ export class FileSystemAccessApiFsClient implements FsClient {
250250
251251 const oldFilepathStat = await this . stat ( oldPath )
252252 if ( oldFilepathStat . isFile ( ) ) {
253- const data = await this . readFile ( oldPath )
254- await this . writeFile ( newPath , data )
255- await this . rm ( oldPath )
253+ await this . renameFile ( oldPath , newPath )
256254 } else if ( oldFilepathStat . isDirectory ( ) ) {
257255 await this . mkdir ( newPath )
258256 const sourceFolder = await this . getDirectoryByPath ( oldPath )
@@ -264,6 +262,40 @@ export class FileSystemAccessApiFsClient implements FsClient {
264262 }
265263 }
266264
265+ private async renameFile ( oldPath : string , newPath : string ) : Promise < void > {
266+ const { folderPath : oldFolder , leafSegment : oldName } = this . getFolderPathAndLeafSegment ( oldPath )
267+ const { folderPath : newFolder , leafSegment : newName } = this . getFolderPathAndLeafSegment ( newPath )
268+
269+ const oldDir = await this . getDirectoryByPath ( oldFolder )
270+ const fileHandle = await this . getEntry < 'file' > ( oldDir , oldName , 'file' )
271+ if ( ! fileHandle ) {
272+ throw new ENOENT ( oldPath )
273+ }
274+
275+ // Strategy 1: Native move() — zero-copy rename, supported in Chrome and Safari OPFS.
276+ // Always pass (directory, newName) form — Safari doesn't support the move(newName) shorthand.
277+ if ( typeof fileHandle . move === 'function' ) {
278+ const newDir = oldFolder === newFolder ? oldDir : await this . getDirectoryByPath ( newFolder )
279+ await fileHandle . move ( newDir , newName )
280+ return
281+ }
282+
283+ // Strategy 2: Streaming copy — read in chunks, write via stream. Never loads entire file.
284+ const CHUNK_SIZE = 1024 * 1024
285+ const file = await fileHandle . getFile ( )
286+ const writable = await this . createWritableStream ( newPath )
287+ let offset = 0
288+ while ( offset < file . size ) {
289+ const end = Math . min ( offset + CHUNK_SIZE , file . size )
290+ const blob = file . slice ( offset , end )
291+ const chunk = new Uint8Array ( await blob . arrayBuffer ( ) )
292+ await writable . write ( chunk )
293+ offset = end
294+ }
295+ await writable . close ( )
296+ await this . rm ( oldPath )
297+ }
298+
267299 /**
268300 * Symlinks are not supported in the current implementation.
269301 * @throws Error: symlinks are not supported.
0 commit comments