char/avril
(wip) why'd you have to go and make things so complicaaaaated?
git clone https://git.t4t.associates/char/avril
e9b1b43
main
1const Readable = globalThis . ReadableStream ; 2const Writable = globalThis . WritableStream ; 3const Transform = globalThis . TransformStream ; 4const ReadableDefaultReader = globalThis . ReadableStreamDefaultReader ; 5const WritableDefaultWriter = globalThis . WritableStreamDefaultWriter ; 6const getReader = Readable . prototype . getReader ; 7const read = ReadableDefaultReader . prototype . read ; 8const cancelReader = ReadableDefaultReader . prototype . cancel ; 9const getWriter = Writable . prototype . getWriter ; 10const write = WritableDefaultWriter . prototype . write ; 11const closeWriter = WritableDefaultWriter . prototype . close ; 12const closeStream = Writable . prototype . close ; 13const abortWriter = WritableDefaultWriter . prototype . abort ; 14const writerClosed = Object . getOwnPropertyDescriptor ( 15WritableDefaultWriter . prototype , 16"closed" , 17) ! 18. get as () => Promise < void >; 19const isExtensible = Object . isExtensible ; 20const transferStates = new WeakMap < object , StreamRecord >(); 21function invoke < F extends ( ...args :never []) => unknown > ( 22fn :F , 23receiver :unknown , 24args :unknown [] = [], 25) :ReturnType < F > { 26return apply ( fn , receiver , args ) as ReturnType < F >; 27} 28function cloneRejection < T > ( promise :Promise < T >) :Promise < T > { 29return then ( promise , undefined , ( error ) => { 30throw cloneError ( error ); 31}); 32} 33function cloneError ( error :unknown ) :unknown { 34try { 35return cloneValue ( error ); 36} catch ( cloneError ) { 37return cloneError ; 38} 39} 40function cancelAfterFailure ( 41record :StreamRecord , 42error :unknown , 43) :Promise < never > { 44let cancellation ; 45try { 46cancellation = invoke ( cancelReader , record . reader , [ error ]); 47} catch { 48throw error ; 49} 50return then ( cancellation , () => { 51throw error ; 52}, () => { 53throw error ; 54}); 55} 56function makeReadable ( record :StreamRecord ) :ReadableResource { 57const source = { 58pull ( controller :ReadController ) { 59const reading = then ( invoke ( read , record . reader ), ( result ) => { 60if ( result . done ) controller . close (); 61else controller . enqueue ( cloneValue ( result . value )); 62}); 63return then ( 64reading , 65undefined , 66( error ) => cancelAfterFailure ( record , cloneError ( error )), 67); 68}, 69cancel ( reason :unknown ) { 70let copied ; 71try { 72copied = cloneValue ( reason ); 73} catch ( error ) { 74return cancelAfterFailure ( record , error ); 75} 76return cloneRejection ( invoke ( cancelReader , record . reader , [ copied ])); 77}, 78}; 79return new Readable ( source , { highWaterMark :0 }); 80} 81function abortAfterFailure ( 82record :StreamRecord , 83error :unknown , 84) :Promise < never > { 85let abort ; 86try { 87abort = invoke ( abortWriter , record . writer , [ error ]); 88} catch { 89throw error ; 90} 91return then ( abort , () => { 92throw error ; 93}, () => { 94throw error ; 95}); 96} 97function makeWritable ( record :StreamRecord ) :WritableResource { 98return new Writable ({ 99start ( controller ) { 100record . controller = controller ; 101}, 102write ( chunk ) { 103let copied ; 104try { 105copied = cloneValue ( chunk ); 106} catch ( error ) { 107return abortAfterFailure ( record , error ); 108} 109return cloneRejection ( invoke ( write , record . writer , [ copied ])); 110}, 111close () { 112if ( record . writableState === "closed" ) { 113return invoke ( writerClosed , record . writer ); 114} 115return cloneRejection ( invoke ( closeWriter , record . writer )); 116}, 117abort ( reason ) { 118let copied ; 119try { 120copied = cloneValue ( reason ); 121} catch ( error ) { 122return abortAfterFailure ( record , error ); 123} 124return cloneRejection ( invoke ( abortWriter , record . writer , [ copied ])); 125}, 126}, { highWaterMark :1 }); 127} 128function prepareMarked ( _state :unknown , source :object ) :StreamResource { 129const record :StreamRecord = { 130 source, 131readable :null , 132writable :null , 133reader :null , 134writer :null , 135}; 136if ( descriptor ( source , "_readableStreamController" )) { 137record . readable = source as ReadableResource ; 138record . kind = "readable" ; 139} else if ( descriptor ( source , "_writableStreamController" )) { 140record . writable = source as WritableResource ; 141record . kind = "writable" ; 142} else if ( descriptor ( source , "_transformStreamController" )) { 143record . readable = ownValue ( source , "_readable" ) as ReadableResource ; 144record . writable = ownValue ( source , "_writable" ) as WritableResource ; 145record . kind = "transform" ; 146} else { 147throw dataCloneError ( "This object is not a transferable stream" ); 148} 149validateRecord ( record ); 150if ( record . readable ) record . readableClone = makeReadable ( record ); 151if ( record . writable ) record . writableClone = makeWritable ( record ); 152let destination :StreamResource | undefined = record . readableClone ; 153if ( record . kind === "transform" ) { 154destination = new Transform (); 155define ( destination , "_readable" , { value :record . readableClone }); 156define ( destination , "_writable" , { value :record . writableClone }); 157} else if ( record . kind === "writable" ) destination = record . writableClone ; 158record . destination = destination ; 159set ( transferStates , destination ! , record ); 160return destination ! ; 161} 162function validateMarked ( 163_state :unknown , 164_source :object , 165destination :StreamResource , 166) :void { 167validateRecord ( get ( transferStates , destination ) ! ); 168} 169function validateRecord ( record :StreamRecord ) :void { 170if ( record . readable ) { 171if ( ! descriptor ( record . readable , "_readableStreamController" )) { 172throw dataCloneError ( "Invalid readable stream state" ); 173} 174validateSlot ( record . readable , "_reader" ); 175} 176if ( record . writable ) { 177if ( ! descriptor ( record . writable , "_writableStreamController" )) { 178throw dataCloneError ( "Invalid writable stream state" ); 179} 180validateSlot ( record . writable , "_writer" ); 181} 182if ( record . writable ) { 183record . writableState = ownValue ( record . writable , "_state" ); 184} 185if ( 186( record . readable && getPrototype ( record . readable ) !== Readable . prototype && 187! isExtensible ( record . readable )) || 188( record . writable && getPrototype ( record . writable ) !== Writable . prototype && 189! isExtensible ( record . writable )) 190) { 191throw dataCloneError ( 192"not yet implemented: transferring this non-extensible stream" , 193); 194} 195} 196function commitMarked ( 197_state :unknown , 198_source :object , 199destination :StreamResource , 200) :void { 201const record = get ( transferStates , destination ) ! ; 202if ( record . readable ) record . reader = acquireReader ( record . readable ); 203if ( record . writable ) record . writer = acquireWriter ( record . writable ); 204if ( record . writable ) { 205if ( record . writableState === "closed" ) { 206const closing = invoke ( closeStream , record . writableClone ); 207markPromiseHandled ( closing ); 208then ( closing , undefined , ( error ) => { 209try { 210record . controller ! . error ( cloneError ( error )); 211} catch ( cloneError ) { 212record . controller ! . error ( cloneError ); 213} 214}); 215} 216then ( invoke ( writerClosed , record . writer ), undefined , ( error ) => { 217try { 218record . controller ! . error ( cloneValue ( error )); 219} catch ( cloneError ) { 220record . controller ! . error ( cloneError ); 221} 222}); 223} 224} 225function validateSlot ( stream :object , key :PropertyKey ) :void { 226const property = descriptor ( stream , key ); 227if ( property ?. value !== undefined ) { 228throw dataCloneError ( "Cannot transfer a locked stream" ); 229} 230if ( ! property || ! ( "value" in property ) || ! property . writable ) { 231throw dataCloneError ( 232"not yet implemented: transferring a stream with immutable internal state" , 233); 234} 235} 236function ownValue ( object :object , key :PropertyKey ) :unknown { 237const property = descriptor ( object , key ); 238return property && "value" in property ?property . value :undefined ; 239} 240function acquireReader ( stream :ReadableResource ) :Reader { 241const prototype = getPrototype ( stream ); 242if ( prototype === Readable . prototype ) return invoke ( getReader , stream ); 243setPrototype ( stream , Readable . prototype ); 244try { 245return invoke ( getReader , stream ); 246} finally { 247setPrototype ( stream , prototype ); 248} 249} 250function acquireWriter ( stream :WritableResource ) :Writer { 251const prototype = getPrototype ( stream ); 252if ( prototype === Writable . prototype ) return invoke ( getWriter , stream ); 253setPrototype ( stream , Writable . prototype ); 254try { 255return invoke ( getWriter , stream ); 256} finally { 257setPrototype ( stream , prototype ); 258} 259} 260registerMarkedTransferable ( prepareMarked , validateMarked , commitMarked );