char/avril
(wip) why'd you have to go and make things so complicaaaaated?
git clone https://git.t4t.associates/char/avril
766372c
main
1import { 2promiseResolve , 3promiseThen as then , 4safePromise , 5} from "ext:core/promises" ; 6import { 7ArrayIsArray , 8DatePrototypeGetTime as getTime , 9getter , 10NumberIsFinite as numberIsFinite , 11ObjectCreate , 12ObjectDefineProperty as define , 13ObjectFreeze as freeze , 14RangeError , 15ReflectApply as apply , 16SymbolIterator , 17TypedArrayPrototypeGetBuffer as bufferOf , 18TypedArrayPrototypeGetByteLength as length , 19TypedArrayPrototypeGetByteOffset as offsetOf , 20TypedArrayPrototypeGetTag as tag , 21TypeError , 22Uint8Array , 23Uint8ArrayPrototypeSet as setBytes , 24uncurryThis , 25WeakMap , 26WeakMapPrototypeGet as get , 27WeakMapPrototypeSet as set , 28} from "ext:core/primordials" ; 29import type { Metadata , WatchEvent } from "ext:core/ops" ; 30import { 31op_file_chmod as chmodOp , 32op_file_chown as chownOp , 33op_file_close as closeOp , 34op_file_copy as copyOp , 35op_file_fchmod as fchmodOp , 36op_file_fchmod_sync as fchmodSyncOp , 37op_file_fchown as fchownOp , 38op_file_fchown_sync as fchownSyncOp , 39op_file_flock as flockOp , 40op_file_flock_sync as flockSyncOp , 41op_file_funlock as funlockOp , 42op_file_funlock_sync as funlockSyncOp , 43op_file_futime as futimeOp , 44op_file_futime_sync as futimeSyncOp , 45op_file_lchown as lchownOp , 46op_file_link as linkOp , 47op_file_lutime as lutimeOp , 48op_file_mkdir as mkdirOp , 49op_file_open as openOp , 50op_file_path_stat as pathStatOp , 51op_file_path_truncate as truncatePathOp , 52op_file_read as readOp , 53op_file_read_dir as readDirOp , 54op_file_read_link as readLinkOp , 55op_file_read_sync as readSyncOp , 56op_file_real_path as realPathOp , 57op_file_remove as removeOp , 58op_file_rename as renameOp , 59op_file_seek as seekOp , 60op_file_seek_sync as seekSyncOp , 61op_file_stat as statOp , 62op_file_stat_sync as statSyncOp , 63op_file_symlink as symlinkOp , 64op_file_sync as syncOp , 65op_file_sync_sync as syncSyncOp , 66op_file_truncate as truncateOp , 67op_file_truncate_sync as truncateSyncOp , 68op_file_url_path as urlPath , 69op_file_utime as utimeOp , 70op_file_watch as watchOp , 71op_file_watch_close as watchCloseOp , 72op_file_watch_keepalive as watchKeepAliveOp , 73op_file_watch_next as watchNextOp , 74op_file_watch_next_unref as watchNextUnrefOp , 75op_file_watch_ref as watchRefOp , 76op_file_write as writeOp , 77op_file_write_sync as writeSyncOp , 78op_file_writev as writevOp , 79op_file_writev_sync as writevSyncOp , 80} from "ext:core/ops" ; 81import { array , dictionary , isObject , required , state } from "ext:web/idl.js" ; 82import { addListener , removeListener , signals } from "ext:web/events.js" ; 83import { registerUncloneable } from "ext:web/serialization.js" ; 84import { URL } from "ext:url/main.js" ; 85import type { 86ReadableByteStreamController , 87WritableStreamDefaultController as WritableController , 88} from "ext:webstreams/streams.js" ; 89import { 90byobRequest , 91enqueue , 92errorStream , 93finish , 94ReadableStream , 95ReadableStreamBYOBRequest , 96WritableStream , 97} from "ext:webstreams/streams.js" ; 98import { errors , failure , result } from "./errors.js" ; 99 100const handles = new WeakMap < object , HandleState >(); 101registerUncloneable ( handles ); 102const watchers = new WeakMap < object , WatchState >(); 103registerUncloneable ( watchers ); 104const Date = globalThis . Date , 105FinalizationRegistry = globalThis . FinalizationRegistry ; 106const register = uncurryThis ( FinalizationRegistry . prototype . register ), 107unregister = uncurryThis ( FinalizationRegistry . prototype . unregister ); 108const registry = new FinalizationRegistry (( id :number ) => 109closeOp ( args ( id , true )) 110); 111const watchRegistry = new FinalizationRegistry (( id :number ) => 112watchCloseOp ( args ( id , true )) 113); 114const urlHref = getter ( URL . prototype , "href" ), 115getView = getter ( ReadableStreamBYOBRequest . prototype , "view" ); 116const writableError = uncurryThis ( 117WritableStreamDefaultController . prototype . error , 118); 119const dispose :typeof Symbol . dispose = Symbol . dispose , 120asyncIterator :typeof Symbol . asyncIterator = Symbol . asyncIterator , 121empty = new Uint8Array (); 122const watchKinds = freeze ( 123[ "create" , "modify" , "remove" , "rename" , "rescan" ] as const , 124); 125const safeInteger = Number . isSafeInteger , 126mathTrunc = Math . trunc , 127mathRound = Math . round ; 128const SeekMode = freeze ({ Start :0 , Current :1 , End :2 }); 129 130function args < T extends unknown [] > ( ...items :T ) :T { 131const result = array (); 132for ( let i = 0 ; i < items . length ; i ++ ) result [ i ] = items [ i ]; 133return result as unknown as T ; 134} 135function path ( value :unknown ) :string { 136if ( typeof value === "string" ) return value ; 137let href ; 138try { 139href = urlHref ( value ); 140} catch { 141throw new TypeError ( "Expected a path string or file URL" ); 142} 143return result ( urlPath ( href )); 144} 145interface HandleState { 146id :number | null ; 147owner :object ; 148readable :ReadableStream < Uint8Array > | null ; 149writable :WritableStream < Uint8Array > | null ; 150reader :ReadableByteStreamController | null ; 151writer :WritableController | null ; 152} 153interface WatchState { 154id :number | null ; 155owner :object ; 156referenced :boolean ; 157epoch :number ; 158signal :unknown ; 159onAbort :(() => void ) | null ; 160} 161function id ( data :{ id :number | null }) :number { 162if ( data . id === null ) throw failure ( "BadResource" , "File handle is closed" ); 163return data . id ; 164} 165function bytes ( value :unknown ) :Uint8Array < ArrayBufferLike > { 166if ( tag ( value ) !== "Uint8Array" ) { 167throw new TypeError ( "File I/O requires a Uint8Array" ); 168} 169return value as Uint8Array < ArrayBufferLike >; 170} 171function segment ( 172buffer :Uint8Array < ArrayBufferLike >, 173offset :number , 174) :Uint8Array < ArrayBufferLike > { 175const remaining = length ( buffer ) - offset ; 176return new Uint8Array ( 177bufferOf ( buffer ), 178offsetOf ( buffer ) + offset , 179remaining < 65536 ?remaining :65536 , 180); 181} 182function vectors ( buffers :unknown ) :Uint8Array < ArrayBuffer >[] { 183if ( ! ArrayIsArray ( buffers )) { 184throw new TypeError ( "File I/O requires an array of Uint8Array" ); 185} 186// Snapshot before queueing, without rereading caller-controlled elements. 187const count = buffers . length , pieces = args < Uint8Array < ArrayBuffer >[]>(); 188if ( ! safeInteger ( count ) || count < 0 || count > 4294967295 ) { 189throw new RangeError ( "Invalid vector length" ); 190} 191let total = 0 ; 192for ( let i = 0 ; i < count ; i ++ ) { 193const view = buffers [ i ] as Uint8Array < ArrayBufferLike >; 194if ( tag ( view ) !== "Uint8Array" ) { 195throw new TypeError ( "File I/O requires an array of Uint8Array" ); 196} 197setBytes ( view , empty ); 198const size = length ( view ); 199if ( size === 0 ) continue ; 200if ( ! safeInteger ( total + size )) { 201throw new RangeError ( "Vector exceeds safe integer range" ); 202} 203total += size ; 204// Fixed snapshot for the native call, which needs unshared storage. 205pieces [ pieces . length ] = new Uint8Array ( 206new Uint8Array ( bufferOf ( view ), offsetOf ( view ), size ), 207); 208} 209return pieces ; 210} 211function integer ( value :unknown , negative = false ) :number { 212if ( ! safeInteger ( value ) || ( ! negative && ( value as number ) < 0 )) { 213throw new RangeError ( "Expected a safe integer" ); 214} 215return value as number ; 216} 217function mode ( value :number ) :number { 218if ( integer ( value ) > 0o7777 ) { 219throw new RangeError ( "Invalid file mode" ); 220} 221return value ; 222} 223function owner ( value :number | null | undefined ) :number | null { 224if ( value === null || value === undefined ) return null ; 225if ( integer ( value ) >= 4294967295 ) { 226throw new RangeError ( "Invalid user or group id" ); 227} 228return value ; 229} 230function stamp ( value :number | Date ) :[ number , number ] { 231let seconds , nanos ; 232if ( value instanceof Date ) { 233const ms = getTime ( value ); 234if ( ! numberIsFinite ( ms )) { 235throw new TypeError ( "Expected a timestamp in seconds or a Date" ); 236} 237// Split integer milliseconds exactly: float seconds would smear the 238// sub-millisecond remainder across the nanosecond field. 239seconds = mathTrunc ( ms / 1000 ); 240nanos = ( ms - seconds * 1000 ) * 1000000 ; 241} else { 242if ( typeof value !== "number" || ! numberIsFinite ( value )) { 243throw new TypeError ( "Expected a timestamp in seconds or a Date" ); 244} 245seconds = mathTrunc ( value ); 246nanos = mathRound (( value - seconds ) * 1e9 ); 247} 248if ( nanos < 0 ) { 249seconds -= 1 ; 250nanos += 1e9 ; 251} else if ( nanos >= 1e9 ) { 252seconds += 1 ; 253nanos -= 1e9 ; 254} 255if ( 256seconds < - 8640000000000 || seconds > 8640000000000 || 257( seconds === 8640000000000 && nanos > 0 ) 258) { 259throw new RangeError ( "Timestamp out of range" ); 260} 261return [ seconds , nanos ]; 262} 263function metadata ( row :Metadata ) { 264return { 265isFile :row [ 0 ], 266isDirectory :row [ 1 ], 267isSymlink :row [ 2 ], 268size :row [ 3 ], 269mtime :row [ 4 ] === null ?null :new Date ( row [ 4 ]), 270atime :row [ 5 ] === null ?null :new Date ( row [ 5 ]), 271birthtime :row [ 6 ] === null ?null :new Date ( row [ 6 ]), 272mode :row [ 7 ], 273uid :row [ 8 ], 274gid :row [ 9 ], 275}; 276} 277function watchId ( data :WatchState ) :number { 278if ( data . id === null ) { 279throw failure ( "BadResource" , "Filesystem watcher is closed" ); 280} 281return data . id ; 282} 283function closeWatcher ( data :WatchState , quiet = false ) :void { 284if ( data . id === null ) { 285if ( ! quiet ) watchId ( data ); 286return ; 287} 288const current = data . id ; 289data . id = null ; 290unregister ( watchRegistry , data . owner ); 291if ( data . signal !== undefined && data . onAbort !== null ) { 292removeListener ( data . signal , "abort" , data . onAbort ); 293} 294data . signal = undefined ; 295data . onAbort = null ; 296result ( watchCloseOp ( args ( current , quiet ))); 297} 298function keepAlive ( data :WatchState , epoch :number ) :void { 299if ( data . id === null ) return ; 300then ( watchKeepAliveOp ( args ( data . id , epoch )), () => {}, () => {}); 301} 302function watchEvent ( row :WatchEvent ) { 303return freeze ({ 304__proto__ :null , 305kind :watchKinds [ row [ 0 ]], 306paths :freeze ( row [ 1 ]), 307}); 308} 309class FsWatcher { 310declare static readonly [ Symbol . hasInstance ] :( value :unknown ) => value isFsWatcher ; 311constructor () { 312throw new TypeError ( "Illegal constructor" ); 313} 314close () { 315closeWatcher ( state ( watchers , this )); 316} 317ref () { 318const data = state ( watchers , this ); 319if ( data . id === null || data . referenced ) return ; 320data . referenced = true ; 321data . epoch = result ( watchRefOp ( data . id )); 322keepAlive ( data , data . epoch ); 323} 324unref () { 325const data = state ( watchers , this ); 326if ( data . id === null || ! data . referenced ) return ; 327data . referenced = false ; 328data . epoch = result ( watchRefOp ( data . id )); 329} 330[ dispose ]() { 331closeWatcher ( state ( watchers , this ), true ); 332} 333[ asyncIterator ]() :AsyncIterableIterator < ReturnType < typeof watchEvent >, undefined > { 334const data = state ( watchers , this ); 335return { 336async next () { 337if ( data . id === null ) return { done :true , value :undefined }; 338try { 339const row = result ( 340await safePromise ( 341( data . referenced ?watchNextOp :watchNextUnrefOp )( 342watchId ( data ), 343), 344), 345); 346if ( row === null || data . id === null ) { 347closeWatcher ( data , true ); 348return { done :true , value :undefined }; 349} 350return { done :false , value :watchEvent ( row ) }; 351} catch ( error ) { 352if ( data . id === null ) return { done :true , value :undefined }; 353closeWatcher ( data , true ); 354throw error ; 355} 356}, 357return () { 358closeWatcher ( data , true ); 359return promiseResolve ({ done :true , value :undefined }); 360}, 361[ asyncIterator ]() { 362return this ; 363}, 364}; 365} 366} 367define ( FsWatcher . prototype , Symbol . toStringTag , { 368value :"FsWatcher" , 369configurable :true , 370}); 371function watchPaths ( value :unknown ) :string [] { 372const filenames = args < string []>(); 373if ( typeof value === "string" ) { 374filenames [ 0 ] = path ( value ); 375return filenames ; 376} 377let href ; 378try { 379href = urlHref ( value ); 380} catch {} 381if ( href !== undefined ) { 382filenames [ 0 ] = path ( value ); 383return filenames ; 384} 385if ( ! isObject ( value )) { 386throw new TypeError ( "Expected a filesystem path or iterable of paths" ); 387} 388const method = value [ SymbolIterator ]; 389if ( typeof method !== "function" ) { 390throw new TypeError ( "Expected a filesystem path or iterable of paths" ); 391} 392const iterator = apply ( method , value , args ()) as { next ?:unknown } | null , 393next = iterator ?. next ; 394if ( ! isObject ( iterator ) || typeof next !== "function" ) { 395throw new TypeError ( "Invalid filesystem path iterator" ); 396} 397let count = 0 ; 398while ( true ) { 399const step = apply ( next , iterator , args ()); 400if ( ! isObject ( step )) { 401throw new TypeError ( "Invalid filesystem path iterator result" ); 402} 403if ( step . done ) break ; 404if ( count >= 128 ) { 405throw new RangeError ( "Too many filesystem watch paths" ); 406} 407filenames [ count ++ ] = path ( step . value ); 408} 409if ( count === 0 ) { 410throw new TypeError ( "Expected at least one filesystem watch path" ); 411} 412return filenames ; 413} 414function watchFs ( paths :unknown , options :unknown = undefined ) :FsWatcher { 415required ( arguments . length , 1 ); 416const filenames = watchPaths ( paths ), 417d :Record < PropertyKey , unknown > = options === undefined 418 ?ObjectCreate ( null ) 419 :dictionary ( options ); 420const recursiveValue = d . recursive , signal = d . signal ; 421if ( recursiveValue !== undefined && typeof recursiveValue !== "boolean" ) { 422throw new TypeError ( "recursive must be boolean" ); 423} 424if ( signal !== undefined && state ( signals , signal ). aborted ) { 425throw state ( signals , signal ). reason ; 426} 427const current = result ( 428watchOp ( args ( filenames , recursiveValue !== false )), 429); 430const watcher = ObjectCreate ( FsWatcher . prototype ) as FsWatcher ; 431const data :WatchState = { 432id :current , 433owner :watcher , 434referenced :true , 435epoch :0 , 436signal :undefined as unknown , 437onAbort :null as (() => void ) | null , 438}; 439set ( watchers , watcher , data ); 440register ( watchRegistry , watcher , current , watcher ); 441keepAlive ( data , 0 ); 442if ( signal !== undefined ) { 443data . signal = signal ; 444data . onAbort = () => closeWatcher ( data , true ); 445addListener ( signal , "abort" , data . onAbort , { once :true }); 446if ( state ( signals , signal ). aborted ) { 447const reason = state ( signals , signal ). reason ; 448closeWatcher ( data , true ); 449throw reason ; 450} 451} 452return watcher ; 453} 454function close ( 455data :HandleState , 456quiet = false , 457source :"read" | "write" | null = null , 458) :void { 459if ( data . id === null ) { 460if ( ! quiet ) id ( data ); 461return ; 462} 463const current = data . id ; 464data . id = null ; 465unregister ( registry , data . owner ); 466const error = failure ( "BadResource" , "File handle is closed" ); 467if ( data . reader && source !== "read" ) errorStream ( data . reader , error ); 468if ( data . writer && source !== "write" ) writableError ( data . writer , error ); 469result ( closeOp ( args ( current , quiet ))); 470} 471class FileHandle { 472declare static readonly [ Symbol . hasInstance ] :( value :unknown ) => value isFileHandle ; 473constructor () { 474throw new TypeError ( "Illegal constructor" ); 475} 476async read ( buffer :Uint8Array < ArrayBufferLike >) :Promise < number | null > { 477const data = state ( handles , this ), current = id ( data ); 478bytes ( buffer ); 479setBytes ( buffer , empty ); 480const count = length ( buffer ), 481chunk = result ( await safePromise ( readOp ( args ( current , count )))); 482setBytes ( buffer , empty ); 483if ( chunk === null ) return null ; 484setBytes ( buffer , chunk ); 485return length ( chunk ); 486} 487async write ( buffer :Uint8Array < ArrayBufferLike >) :Promise < number > { 488const current = id ( state ( handles , this )); 489const snapshot = new Uint8Array ( segment ( bytes ( buffer ), 0 )); 490return result ( await safePromise ( writeOp ( args ( current , snapshot )))); 491} 492async writev ( buffers :readonly Uint8Array < ArrayBufferLike >[]) :Promise < number > { 493const current = id ( state ( handles , this )); 494const snapshot = vectors ( buffers ); 495return result ( await safePromise ( writevOp ( args ( current , snapshot )))); 496} 497async seek ( offset :number , whence :number ) :Promise < number > { 498const current = id ( state ( handles , this )); 499integer ( offset , true ); 500if ( whence !== 0 && whence !== 1 && whence !== 2 ) { 501throw new TypeError ( "Invalid seek mode" ); 502} 503return result ( await safePromise ( seekOp ( args ( current , offset , whence )))); 504} 505async stat () { 506return metadata ( 507result ( await safePromise ( statOp ( id ( state ( handles , this ))))), 508); 509} 510async truncate ( length = 0 ) { 511result ( 512await safePromise ( 513truncateOp ( args ( id ( state ( handles , this )), integer ( length ))), 514), 515); 516} 517async sync () { 518result ( await safePromise ( syncOp ( args ( id ( state ( handles , this )), false )))); 519} 520async syncData () { 521result ( await safePromise ( syncOp ( args ( id ( state ( handles , this )), true )))); 522} 523async fchmod ( fileMode :number ) :Promise < void > { 524result ( 525await safePromise ( 526fchmodOp ( args ( id ( state ( handles , this )), mode ( fileMode ))), 527), 528); 529} 530async fchown ( 531uid :number | null | undefined , 532gid :number | null | undefined , 533) :Promise < void > { 534result ( 535await safePromise ( 536fchownOp ( args ( id ( state ( handles , this )), owner ( uid ), owner ( gid ))), 537), 538); 539} 540async futime ( atime :number | Date , mtime :number | Date ) :Promise < void > { 541const before = stamp ( atime ), after = stamp ( mtime ); 542result ( 543await safePromise ( 544futimeOp ( 545args ( 546id ( state ( handles , this )), 547before [ 0 ], 548before [ 1 ], 549after [ 0 ], 550after [ 1 ], 551), 552), 553), 554); 555} 556async flock ( exclusive = true ) { 557if ( exclusive !== true && exclusive !== false ) { 558throw new TypeError ( "Lock mode must be boolean" ); 559} 560// Waiting releases the handle queue, so a lock no longer serializes with 561// reads and writes: fire-and-forget flock();write() runs unordered, and a 562// close() landing in the same tick must not observe success past close. 563result ( 564await safePromise ( flockOp ( args ( id ( state ( handles , this )), exclusive ))), 565); 566id ( state ( handles , this )); 567} 568async funlock () { 569result ( await safePromise ( funlockOp ( id ( state ( handles , this ))))); 570} 571readSync ( buffer :Uint8Array < ArrayBufferLike >) :number | null { 572const data = state ( handles , this ), current = id ( data ); 573bytes ( buffer ); 574setBytes ( buffer , empty ); 575const count = length ( buffer ), 576chunk = result ( readSyncOp ( args ( current , count ))); 577setBytes ( buffer , empty ); 578if ( chunk === null ) return null ; 579setBytes ( buffer , chunk ); 580return length ( chunk ); 581} 582writeSync ( buffer :Uint8Array < ArrayBufferLike >) :number { 583const current = id ( state ( handles , this )); 584const snapshot = new Uint8Array ( segment ( bytes ( buffer ), 0 )); 585return result ( writeSyncOp ( args ( current , snapshot ))); 586} 587writevSync ( buffers :readonly Uint8Array < ArrayBufferLike >[]) :number { 588const current = id ( state ( handles , this )); 589const snapshot = vectors ( buffers ); 590return result ( writevSyncOp ( args ( current , snapshot ))); 591} 592seekSync ( offset :number , whence :number ) :number { 593const current = id ( state ( handles , this )); 594integer ( offset , true ); 595if ( whence !== 0 && whence !== 1 && whence !== 2 ) { 596throw new TypeError ( "Invalid seek mode" ); 597} 598return result ( seekSyncOp ( args ( current , offset , whence ))); 599} 600statSync () { 601return metadata ( 602result ( statSyncOp ( id ( state ( handles , this )))), 603); 604} 605truncateSync ( length = 0 ) { 606result ( 607truncateSyncOp ( args ( id ( state ( handles , this )), integer ( length ))), 608); 609} 610syncSync () { 611result ( syncSyncOp ( args ( id ( state ( handles , this )), false ))); 612} 613syncDataSync () { 614result ( syncSyncOp ( args ( id ( state ( handles , this )), true ))); 615} 616fchmodSync ( fileMode :number ) :void { 617result ( 618fchmodSyncOp ( args ( id ( state ( handles , this )), mode ( fileMode ))), 619); 620} 621fchownSync ( 622uid :number | null | undefined , 623gid :number | null | undefined , 624) :void { 625result ( 626fchownSyncOp ( args ( id ( state ( handles , this )), owner ( uid ), owner ( gid ))), 627); 628} 629futimeSync ( atime :number | Date , mtime :number | Date ) :void { 630const before = stamp ( atime ), after = stamp ( mtime ); 631result ( 632futimeSyncOp ( 633args ( 634id ( state ( handles , this )), 635before [ 0 ], 636before [ 1 ], 637after [ 0 ], 638after [ 1 ], 639), 640), 641); 642} 643flockSync ( exclusive = true ) { 644if ( exclusive !== true && exclusive !== false ) { 645throw new TypeError ( "Lock mode must be boolean" ); 646} 647result ( 648flockSyncOp ( args ( id ( state ( handles , this )), exclusive )), 649); 650id ( state ( handles , this )); 651} 652funlockSync () { 653result ( funlockSyncOp ( id ( state ( handles , this )))); 654} 655close () { 656close ( state ( handles , this )); 657} 658[ dispose ]() { 659close ( state ( handles , this ), true ); 660} 661get readable () { 662const data = state ( handles , this ); 663if ( data . readable ) return data . readable ; 664id ( data ); 665data . readable = new ReadableStream ({ 666type :"bytes" , 667start ( controller ) { 668data . reader = controller ; 669}, 670async pull ( controller ) { 671try { 672const request = byobRequest ( controller ), 673view = request && getView ( request ); 674const chunk = result < Uint8Array < ArrayBuffer > | null >( 675await safePromise ( 676readOp ( args ( id ( data ), view ?length ( view ) :65536 )), 677), 678); 679if ( data . id === null ) return ; 680if ( chunk === null ) { 681finish ( controller ); 682close ( data , true , "read" ); 683} else if ( request ) { 684setBytes ( view as Uint8Array < ArrayBuffer >, chunk ); 685respond ( request , length ( chunk )); 686} else enqueue ( controller , chunk ); 687} catch ( error ) { 688errorStream ( controller , error ); 689close ( data , true , "read" ); 690} 691}, 692cancel () { 693close ( data , true , "read" ); 694}, 695}, { highWaterMark :0 }); 696return data . readable ; 697} 698get writable () { 699const data = state ( handles , this ); 700if ( data . writable ) return data . writable ; 701id ( data ); 702data . writable = new WritableStream ({ 703start ( controller ) { 704data . writer = controller ; 705}, 706async write ( buffer :unknown ) { 707try { 708const snapshot = new Uint8Array ( bytes ( buffer )); 709let offset = 0 ; 710do { 711const count = result ( 712await safePromise ( 713writeOp ( 714args ( 715id ( data ), 716segment ( snapshot , offset ) as Uint8Array < ArrayBuffer >, 717), 718), 719), 720); 721if ( 722! safeInteger ( count ) || count < 0 || 723count > length ( snapshot ) - offset || 724( count === 0 && offset < length ( snapshot )) 725) throw failure ( "IoError" , "Invalid partial write" ); 726offset += count ; 727} while ( offset < length ( snapshot )); 728} catch ( error ) { 729close ( data , true , "write" ); 730throw error ; 731} 732}, 733close () { 734close ( data , true , "write" ); 735}, 736abort () { 737close ( data , true , "write" ); 738}, 739}, { 740highWaterMark :65536 , 741size ( chunk ) { 742return tag ( chunk ) === "Uint8Array" ?length ( chunk ) :1 ; 743}, 744}); 745return data . writable ; 746} 747} 748const respond = uncurryThis ( ReadableStreamBYOBRequest . prototype . respond ); 749define ( FileHandle . prototype , Symbol . toStringTag , { 750value :"FileHandle" , 751configurable :true , 752}); 753 754function openArgs ( 755pathname :unknown , 756options :unknown , 757) :[ 758string , 759[ boolean , boolean , boolean , boolean , boolean , boolean , number | null ], 760] { 761const filename = path ( pathname ), 762d = options === undefined ?{ read :true } :dictionary ( options ); 763const flags :( boolean | number | null )[] = array (); 764const keys = [ "read" , "write" , "append" , "truncate" , "create" , "createNew" ]; 765for ( let index = 0 ; index < keys . length ; index ++ ) { 766const key = keys [ index ], value = d [ key ]; 767if ( value !== undefined && typeof value !== "boolean" ) { 768throw new TypeError ( "Open flags must be boolean" ); 769} 770flags [ flags . length ] = value === true ; 771} 772const mode = d . mode ; 773if ( mode !== undefined && integer ( mode ) > 0o7777 ) { 774throw new RangeError ( "Invalid file creation mode" ); 775} 776flags [ flags . length ] = mode === undefined ?null :mode as number ; 777return args ( 778filename , 779flags as [ 780boolean , 781boolean , 782boolean , 783boolean , 784boolean , 785boolean , 786number | null , 787], 788); 789} 790function writeOptions ( options :unknown ) :Record < PropertyKey , unknown > { 791const input = dictionary ( options ), 792output :Record < PropertyKey , unknown > = ObjectCreate ( null ); 793const keys = [ "append" , "create" , "createNew" ]; 794for ( let index = 0 ; index < keys . length ; index ++ ) { 795const key = keys [ index ], value = input [ key ]; 796if ( value !== undefined && typeof value !== "boolean" ) { 797throw new TypeError ( "Write flags must be boolean" ); 798} 799output [ key ] = value === undefined ?key === "create" :value ; 800} 801const mode = input . mode ; 802if ( mode !== undefined && integer ( mode ) > 0o7777 ) { 803throw new RangeError ( "Invalid file creation mode" ); 804} 805output . mode = mode ; 806output . write = true ; 807output . truncate = ! output . append ; 808return output ; 809} 810function fileHandle ( current :number ) :FileHandle { 811const handle = ObjectCreate ( FileHandle . prototype ) as FileHandle ; 812set ( handles , handle , { 813id :current , 814owner :handle , 815readable :null , 816writable :null , 817reader :null , 818writer :null , 819}); 820register ( registry , handle , current , handle ); 821return handle ; 822} 823async function open ( 824pathname :unknown , 825options :unknown = undefined , 826) :Promise < FileHandle > { 827required ( arguments . length , 1 ); 828return fileHandle ( 829result ( await safePromise ( openOp ( openArgs ( pathname , options )))), 830); 831} 832async function create ( pathname :unknown ) :Promise < FileHandle > { 833required ( arguments . length , 1 ); 834return await safePromise ( open ( pathname , { 835read :true , 836write :true , 837create :true , 838truncate :true , 839})); 840} 841async function stat ( pathname :unknown ) { 842required ( arguments . length , 1 ); 843return metadata ( 844result ( await safePromise ( pathStatOp ( args ( path ( pathname ), true )))), 845); 846} 847async function lstat ( pathname :unknown ) { 848required ( arguments . length , 1 ); 849return metadata ( 850result ( await safePromise ( pathStatOp ( args ( path ( pathname ), false )))), 851); 852} 853async function * readDir ( 854pathname :unknown , 855) :AsyncGenerator < 856{ name :string ; isFile :boolean ; isDirectory :boolean ; isSymlink :boolean } 857> { 858required ( arguments . length , 1 ); 859const entries = result ( await safePromise ( readDirOp ( path ( pathname )))); 860for ( let i = 0 ; i < entries . length ; i ++ ) { 861const row = entries [ i ]; 862yield { 863name :row [ 0 ], 864isFile :row [ 1 ], 865isDirectory :row [ 2 ], 866isSymlink :row [ 3 ], 867}; 868} 869} 870async function mkdir ( 871pathname :unknown , 872options :unknown = undefined , 873) :Promise < void > { 874required ( arguments . length , 1 ); 875const filename = path ( pathname ), mode = dictionary ( options ). mode ; 876if ( mode !== undefined && integer ( mode ) > 0o7777 ) { 877throw new RangeError ( "Invalid directory mode" ); 878} 879result ( 880await safePromise ( 881mkdirOp ( args ( filename , mode === undefined ?null :mode as number )), 882), 883); 884} 885async function remove ( 886pathname :unknown , 887options :unknown = undefined , 888) :Promise < void > { 889required ( arguments . length , 1 ); 890const filename = path ( pathname ), recursive = dictionary ( options ). recursive ; 891if ( recursive !== undefined && typeof recursive !== "boolean" ) { 892throw new TypeError ( "recursive must be boolean" ); 893} 894result ( await safePromise ( removeOp ( args ( filename , recursive === true )))); 895} 896async function rename ( source :unknown , destination :unknown ) :Promise < void > { 897required ( arguments . length , 2 ); 898result ( await safePromise ( renameOp ( args ( path ( source ), path ( destination ))))); 899} 900async function copyFile ( source :unknown , destination :unknown ) :Promise < void > { 901required ( arguments . length , 2 ); 902result ( await safePromise ( copyOp ( args ( path ( source ), path ( destination ))))); 903} 904async function symlink ( 905target :unknown , 906linkPath :unknown , 907options :unknown = undefined , 908) :Promise < void > { 909required ( arguments . length , 2 ); 910const contents = path ( target ), 911link = path ( linkPath ), 912kind = dictionary ( options ). type ; 913if ( kind !== undefined && kind !== "file" && kind !== "dir" ) { 914throw new TypeError ( "Symlink type must be file or dir" ); 915} 916result ( 917await safePromise ( 918symlinkOp ( args ( contents , link , kind === undefined ?null :kind )), 919), 920); 921} 922async function readLink ( pathname :unknown ) :Promise < string > { 923required ( arguments . length , 1 ); 924return result ( await safePromise ( readLinkOp ( path ( pathname )))); 925} 926async function realPath ( pathname :unknown ) :Promise < string > { 927required ( arguments . length , 1 ); 928return result ( await safePromise ( realPathOp ( path ( pathname )))); 929} 930async function chmod ( pathname :unknown , fileMode :number ) :Promise < void > { 931required ( arguments . length , 2 ); 932result ( await safePromise ( chmodOp ( args ( path ( pathname ), mode ( fileMode ))))); 933} 934async function chown ( 935pathname :unknown , 936uid :number | null | undefined , 937gid :number | null | undefined , 938) :Promise < void > { 939required ( arguments . length , 3 ); 940result ( 941await safePromise ( chownOp ( args ( path ( pathname ), owner ( uid ), owner ( gid )))), 942); 943} 944async function lchown ( 945pathname :unknown , 946uid :number | null | undefined , 947gid :number | null | undefined , 948) :Promise < void > { 949required ( arguments . length , 3 ); 950result ( 951await safePromise ( lchownOp ( args ( path ( pathname ), owner ( uid ), owner ( gid )))), 952); 953} 954async function utime ( 955pathname :unknown , 956atime :number | Date , 957mtime :number | Date , 958) :Promise < void > { 959required ( arguments . length , 3 ); 960const before = stamp ( atime ), after = stamp ( mtime ); 961result ( 962await safePromise ( 963utimeOp ( 964args ( path ( pathname ), before [ 0 ], before [ 1 ], after [ 0 ], after [ 1 ]), 965), 966), 967); 968} 969async function lutime ( 970pathname :unknown , 971atime :number | Date , 972mtime :number | Date , 973) :Promise < void > { 974required ( arguments . length , 3 ); 975const before = stamp ( atime ), after = stamp ( mtime ); 976result ( 977await safePromise ( 978lutimeOp ( 979args ( path ( pathname ), before [ 0 ], before [ 1 ], after [ 0 ], after [ 1 ]), 980), 981), 982); 983} 984async function link ( oldpath :unknown , newpath :unknown ) :Promise < void > { 985required ( arguments . length , 2 ); 986result ( await safePromise ( linkOp ( args ( path ( oldpath ), path ( newpath ))))); 987} 988async function truncate ( pathname :unknown , length = 0 ) :Promise < void > { 989required ( arguments . length , 1 ); 990result ( 991await safePromise ( truncatePathOp ( args ( path ( pathname ), integer ( length )))), 992); 993} 994export { 995chmod , 996chown , 997copyFile , 998create , 999errors , 1000FileHandle , 1001FsWatcher , 1002lchown , 1003link , 1004lstat , 1005lutime , 1006mkdir , 1007open , 1008readDir , 1009readLink , 1010realPath , 1011remove , 1012rename , 1013SeekMode , 1014stat , 1015symlink , 1016truncate , 1017utime , 1018watchFs , 1019}; 1020export function processHandle ( value :unknown ) :number { 1021return id ( state ( handles , value )); 1022} 1023export function responseHandle ( value :unknown ) :number | null { 1024const data = get ( handles , value ); 1025return data === undefined ?null :id ( data ); 1026} 1027export function detachResponseHandle ( value :unknown ) :void { 1028close ( state ( handles , value ), true ); 1029} 1030export { 1031args , 1032fileHandle , 1033integer , 1034metadata , 1035mode , 1036openArgs , 1037owner , 1038path , 1039stamp , 1040writeOptions , 1041};