import * as fs from "fs" ; import { callbackify } from "util" ; import { Batch , ClientProxy , ClientServerProxy , EncodingOptions , EncodingOptionsCallback } from "../common/proxy" ; import { FsModuleProxy , ReadStreamProxy , Stats as IStats , WatcherProxy , WriteStreamProxy } from "../server/fs" ; import { Readable , Writable } from "./stream" ; class Stats implements fs . Stats { public constructor ( private readonly stats ) { } public get dev ( ) : number { return this . stats . dev ; } public get ino ( ) : number { return this . stats . ino ; } public get mode ( ) : number { return this . stats . mode ; } public get nlink ( ) : number { return this . stats . nlink ; } public get uid ( ) : number { return this . stats . uid ; } public get gid ( ) : number { return this . stats . gid ; } public get rdev ( ) : number { return this . stats . rdev ; } public get size ( ) : number { return this . stats . size ; } public get blksize ( ) : number { return this . stats . blksize ; } public get blocks ( ) : number { return this . stats . blocks ; } public get atime ( ) : Date { return this . stats . atime ; } public get mtime ( ) : Date { return this . stats . mtime ; } public get ctime ( ) : Date { return this . stats . ctime ; } public get birthtime ( ) : Date { return this . stats . birthtime ; } public get atimeMs ( ) : number { return this . stats . atimeMs ; } public get mtimeMs ( ) : number { return this . stats . mtimeMs ; } public get ctimeMs ( ) : number { return this . stats . ctimeMs ; } public get birthtimeMs ( ) : number { return this . stats . birthtimeMs ; } public isFile ( ) { return this . stats . _isFile ; } public isDirectory ( ) { return this . stats . _isDirectory ; } public isBlockDevice ( ) { return this . stats . _isBlockDevice ; } public isCharacterDevice ( ) { return this . stats . _isCharacterDevice ; } public isSymbolicLink ( ) { return this . stats . _isSymbolicLink ; } public isFIFO ( ) { return this . stats . _isFIFO ; } public isSocket ( ) { return this . stats . _isSocket ; } public toObject ( ) { return JSON . parse ( JSON . stringify ( this ) ) ; } } class StatBatch extends Batch < IStats , { path : fs . PathLike ; } > { public constructor ( private readonly proxy ) { super ( ) ; } protected remoteCall ( batch ) { return this . proxy . statBatch ( batch ) ; } } class LstatBatch extends Batch < IStats , { path : fs . PathLike ; } > { public constructor ( private readonly proxy ) { super ( ) ; } protected remoteCall ( batch ) { return this . proxy . lstatBatch ( batch ) ; } } class ReaddirBatch extends Batch < Buffer [ ] | fs . Dirent [ ] | string [ ] , { path : fs . PathLike ; options : EncodingOptions ; } > { public constructor ( private readonly proxy ) { super ( ) ; } protected remoteCall ( queue ) { return this . proxy . readdirBatch ( queue ) ; } } interface ClientWatcherProxy extends WatcherProxy , ClientServerProxy < fs . FSWatcher > { } class Watcher extends ClientProxy < ClientWatcherProxy > implements fs . FSWatcher { public close ( ) { this . catch ( this . proxy . close ( ) ) ; } protected handleDisconnect ( ) { this . emit ( "close" ) ; } } interface ClientReadStreamProxy extends ReadStreamProxy , ClientServerProxy < fs . ReadStream > { } class ReadStream extends Readable < ClientReadStreamProxy > implements fs . ReadStream { public get bytesRead ( ) : number { throw new Error ( "not implemented" ) ; } public get path ( ) : string | Buffer { throw new Error ( "not implemented" ) ; } public close ( ) { this . catch ( this . proxy . close ( ) ) ; } } interface ClientWriteStreamProxy extends WriteStreamProxy , ClientServerProxy < fs . WriteStream > { } class WriteStream extends Writable < ClientWriteStreamProxy > implements fs . WriteStream { public get bytesWritten ( ) : number { throw new Error ( "not implemented" ) ; } public get path ( ) : string | Buffer { throw new Error ( "not implemented" ) ; } public close ( ) { this . catch ( this . proxy . close ( ) ) ; } } interface ClientFsModuleProxy extends FsModuleProxy , ClientServerProxy { createReadStream ( path , options ? ) : Promise < ClientReadStreamProxy > ; createWriteStream ( path , options ? ) : Promise < ClientWriteStreamProxy > ; watch ( filename , options ? ) : Promise < ClientWatcherProxy > ; } export class FsModule { private readonly statBatch : StatBatch ; private readonly lstatBatch : LstatBatch ; private readonly readdirBatch : ReaddirBatch ; public constructor ( private readonly proxy ) { this . statBatch = new StatBatch ( this . proxy ) ; this . lstatBatch = new LstatBatch ( this . proxy ) ; this . readdirBatch = new ReaddirBatch ( this . proxy ) ; } public access = ( path , mode , callback ? ) : void => { if ( typeof mode === "function" ) { callback = mode ; mode = undefined ; } callbackify ( this . proxy . access ) ( path , mode , callback ! ) ; } ; public appendFile = ( path , data , options ? , callback ? ) : void => { if ( typeof options === "function" ) { callback = options ; options = undefined ; } callbackify ( this . proxy . appendFile ) ( path , data , options , callback ! ) ; } ; public chmod = ( path , mode , callback ) : void => { callbackify ( this . proxy . chmod ) ( path , mode , callback ! ) ; } ; public chown = ( path , uid , gid , callback ) : void => { callbackify ( this . proxy . chown ) ( path , uid , gid , callback ! ) ; } ; public close = ( fd , callback ) : void => { callbackify ( this . proxy . close ) ( fd , callback ! ) ; } ; public copyFile = ( src , dest , flags , callback ? ) : void => { if ( typeof flags === "function" ) { callback = flags ; } callbackify ( this . proxy . copyFile ) ( src , dest , typeof flags !== "function" ? flags : undefined , callback ! ) ; } ; public createReadStream = ( path , options ? ) : fs . ReadStream => { return new ReadStream ( this . proxy . createReadStream ( path , options ) ) ; } ; public createWriteStream = ( path , options ? ) : fs . WriteStream => { return new WriteStream ( this . proxy . createWriteStream ( path , options ) ) ; } ; public exists = ( path , callback ) : void => { this . proxy . exists ( path ) . then ( ( exists ) => callback ( exists ) ) . catch ( ( ) => callback ( false ) ) ; } ; public fchmod = ( fd , mode , callback ) : void => { callbackify ( this . proxy . fchmod ) ( fd , mode , callback ! ) ; } ; public fchown = ( fd , uid , gid , callback ) : void => { callbackify ( this . proxy . fchown ) ( fd , uid , gid , callback ! ) ; } ; public fdatasync = ( fd , callback ) : void => { callbackify ( this . proxy . fdatasync ) ( fd , callback ! ) ; } ; public fstat = ( fd , callback ) : void => { callbackify ( this . proxy . fstat ) ( fd , ( error , stats ) => { callback ( error ! , stats && new Stats ( stats ) ) ; } ) ; } ; public fsync = ( fd , callback ) : void => { callbackify ( this . proxy . fsync ) ( fd , callback ! ) ; } ; public ftruncate = ( fd , len , callback ? ) : void => { if ( typeof len === "function" ) { callback = len ; len = undefined ; } callbackify ( this . proxy . ftruncate ) ( fd , len , callback ! ) ; } ; public futimes = ( fd , atime , mtime , callback ) : void => { callbackify ( this . proxy . futimes ) ( fd , atime , mtime , callback ! ) ; } ; public lchmod = ( path , mode , callback ) : void => { callbackify ( this . proxy . lchmod ) ( path , mode , callback ! ) ; } ; public lchown = ( path , uid , gid , callback ) : void => { callbackify ( this . proxy . lchown ) ( path , uid , gid , callback ! ) ; } ; public link = ( existingPath , newPath , callback ) : void => { callbackify ( this . proxy . link ) ( existingPath , newPath , callback ! ) ; } ; public lstat = ( path , callback ) : void => { callbackify ( this . lstatBatch . add ) ( { path } , ( error , stats ) => { callback ( error ! , stats && new Stats ( stats ) ) ; } ) ; } ; public mkdir = ( path , mode , callback ? ) : void => { if ( typeof mode === "function" ) { callback = mode ; mode = undefined ; } callbackify ( this . proxy . mkdir ) ( path , mode , callback ! ) ; } ; public mkdtemp = ( prefix , options , callback ? ) : void => { if ( typeof options === "function" ) { callback = options ; options = undefined ; } callbackify ( this . proxy . mkdtemp ) ( prefix , options , callback ! ) ; } ; public open = ( path , flags , mode , callback ? ) : void => { if ( typeof mode === "function" ) { callback = mode ; mode = undefined ; } callbackify ( this . proxy . open ) ( path , flags , mode , callback ! ) ; } ; public read = ( fd , buffer , offset , length , position , callback ) : void => { this . proxy . read ( fd , length , position ) . then ( ( response ) => { buffer . set ( response . buffer , offset ) ; callback ( undefined ! , response . bytesRead , response . buffer ) ; } ) . catch ( ( error ) => { callback ( error , undefined ! , undefined ! ) ; } ) ; } ; public readFile = ( path , options , callback ? ) : void => { if ( typeof options === "function" ) { callback = options ; options = undefined ; } callbackify ( this . proxy . readFile ) ( path , options , callback ! ) ; } ; public readdir = ( path , options , callback ? ) : void => { if ( typeof options === "function" ) { callback = options ; options = undefined ; } callbackify ( this . readdirBatch . add ) ( { path , options } , callback ! ) ; } ; public readlink = ( path , options , callback ? ) : void => { if ( typeof options === "function" ) { callback = options ; options = undefined ; } callbackify ( this . proxy . readlink ) ( path , options , callback ! ) ; } ; public realpath = ( path , options , callback ? ) : void => { if ( typeof options === "function" ) { callback = options ; options = undefined ; } callbackify ( this . proxy . realpath ) ( path , options , callback ! ) ; } ; public rename = ( oldPath , newPath , callback ) : void => { callbackify ( this . proxy . rename ) ( oldPath , newPath , callback ! ) ; } ; public rmdir = ( path , callback ) : void => { callbackify ( this . proxy . rmdir ) ( path , callback ! ) ; } ; public stat = ( path , callback ) : void => { callbackify ( this . statBatch . add ) ( { path } , ( error , stats ) => { callback ( error ! , stats && new Stats ( stats ) ) ; } ) ; } ; public symlink = ( target , path , type , callback ? ) : void => { if ( typeof type === "function" ) { callback = type ; type = undefined ; } callbackify ( this . proxy . symlink ) ( target , path , type , callback ! ) ; } ; public truncate = ( path , len , callback ? ) : void => { if ( typeof len === "function" ) { callback = len ; len = undefined ; } callbackify ( this . proxy . truncate ) ( path , len , callback ! ) ; } ; public unlink = ( path , callback ) : void => { callbackify ( this . proxy . unlink ) ( path , callback ! ) ; } ; public utimes = ( path , atime , mtime , callback ) : void => { callbackify ( this . proxy . utimes ) ( path , atime , mtime , callback ! ) ; } ; public write = ( fd , buffer , offset , length , position , callback ? ) : void => { if ( typeof offset === "function" ) { callback = offset ; offset = undefined ; } if ( typeof length === "function" ) { callback = length ; length = undefined ; } if ( typeof position === "function" ) { callback = position ; position = undefined ; } this . proxy . write ( fd , buffer , offset , length , position ) . then ( ( r ) => { callback ! ( undefined ! , r . bytesWritten , r . buffer ) ; } ) . catch ( ( error ) => { callback ! ( error , undefined ! , undefined ! ) ; } ) ; } ; public writeFile = ( path , data , options , callback ? ) : void => { if ( typeof options === "function" ) { callback = options ; options = undefined ; } callbackify ( this . proxy . writeFile ) ( path , data , options , callback ! ) ; } ; public watch = ( filename , options ? , listener ? ) : fs . FSWatcher => { if ( typeof options === "function" ) { listener = options ; options = undefined ; } const watcher = new Watcher ( this . proxy . watch ( filename , options ) ) ; if ( listener ) { watcher . on ( "change" , listener ) ; } return watcher ; } ; }