char/avril
(wip) why'd you have to go and make things so complicaaaaated?
git clone https://git.t4t.associates/char/avril
c013baf
main
1import { markUncloneable , registerUncloneable } from "ext:web/serialization.js" ; 2import { 3Array , 4ArrayPrototypePush as push , 5IteratorPrototype , 6ObjectCreate , 7ObjectDefineProperty , 8ObjectSetPrototypeOf , 9ReflectApply as apply , 10SymbolToStringTag , 11TypeError , 12WeakMap , 13WeakMapPrototypeGet as get , 14WeakMapPrototypeSet as set , 15} from "ext:core/primordials" ; 16import { array , required , state , usv } from "ext:web/idl.js" ; 17import { blobs } from "ext:blob/blob.js" ; 18import { fileEntry } from "ext:blob/file.js" ; 19 20import type { File } from "ext:blob/file.js" ; 21 22type FormValue = string | File ; 23interface FormEntry { 24name :string ; 25value :FormValue ; 26} 27interface FormState { 28list :FormEntry []; 29} 30type IteratorValues = { 0 :[ string , FormValue ]; 1 :string ; 2 :FormValue }; 31 32const forms = new WeakMap < object , FormState >(), arrayPrototype = Array . prototype ; 33registerUncloneable ( forms ); 34function entry ( rawName :unknown , value :unknown , rawFilename :unknown , count :number ) :FormEntry { 35const name = usv ( rawName ); 36const blob = get ( blobs , value ); 37if ( count >= 3 && ! blob ) { 38throw new TypeError ( "Expected a Blob when filename is supplied" ); 39} 40if ( ! blob ) return { name, value :usv ( value ) }; 41const filename = rawFilename === undefined ?undefined :usv ( rawFilename ); 42return { name, value :fileEntry ( value , filename ) }; 43} 44// Iterator inheritance is installed below, without introducing a super call. 45interface FormDataIterator < K extends keyof IteratorValues > extends IterableIterator < IteratorValues [ K ], undefined , unknown > {} 46class FormDataIterator < K extends keyof IteratorValues > { 47 #data:FormState ; 48 #index= 0 ; 49 #kind:K ; 50constructor ( data :FormState , kind :K ) { 51markUncloneable ( this ); 52this . #data= data ; 53this . #kind= kind ; 54} 55next () :IteratorResult < IteratorValues [ K ], undefined > { 56const data = this . #data; 57if ( this . #index>= data . list . length ) { 58return { value :undefined , done :true }; 59} 60const e = data . list [ this . #index++ ]; 61return { 62value :( this . #kind=== 0 63 ?[ e . name , e . value ] 64 :this . #kind=== 1 65 ?e . name 66 :e . value ) as IteratorValues [ K ], 67done :false , 68}; 69} 70} 71delete ( FormDataIterator . prototype as { constructor ?:Function }). constructor ; 72ObjectSetPrototypeOf ( FormDataIterator . prototype , IteratorPrototype ); 73ObjectDefineProperty ( FormDataIterator . prototype , "next" , { enumerable :true }); 74ObjectDefineProperty ( FormDataIterator . prototype , SymbolToStringTag , { 75value :"FormData Iterator" , 76configurable :true , 77}); 78interface FormData { 79[ Symbol . iterator ]() :FormDataIterator < 0 >; 80} 81class FormData { 82constructor ( form :unknown = undefined , submitter :unknown = null ) { 83if ( form !== undefined || ( submitter !== undefined && submitter !== null )) { 84throw new TypeError ( "not yet implemented: DOM-backed FormData" ); 85} 86set ( forms , this , { list :array < FormEntry >() }); 87} 88append ( name :unknown , value :unknown , filename :unknown = undefined ) :void { 89const data = state ( forms , this ); 90required ( arguments . length , 2 ); 91push ( data . list , entry ( name , value , filename , arguments . length )); 92} 93set ( name :unknown , value :unknown , filename :unknown = undefined ) :void { 94const data = state ( forms , this ); 95required ( arguments . length , 2 ); 96const e = entry ( name , value , filename , arguments . length ); 97let found = false , write = 0 ; 98for ( let i = 0 ; i < data . list . length ; i ++ ) { 99const current = data . list [ i ]; 100if ( current . name !== e . name ) data . list [ write ++ ] = current ; 101else if ( ! found ) { 102data . list [ write ++ ] = e ; 103found = true ; 104} 105} 106data . list . length = write ; 107if ( ! found ) push ( data . list , e ); 108} 109delete ( rawName :unknown ) :void { 110const data = state ( forms , this ); 111required ( arguments . length , 1 ); 112const name = usv ( rawName ); 113let write = 0 ; 114for ( let i = 0 ; i < data . list . length ; i ++ ) { 115const current = data . list [ i ]; 116if ( current . name !== name ) data . list [ write ++ ] = current ; 117} 118data . list . length = write ; 119} 120get ( rawName :unknown ) :FormValue | null { 121const data = state ( forms , this ); 122required ( arguments . length , 1 ); 123const name = usv ( rawName ); 124for ( let i = 0 ; i < data . list . length ; i ++ ) { 125if ( data . list [ i ]. name === name ) return data . list [ i ]. value ; 126} 127return null ; 128} 129getAll ( rawName :unknown ) :FormValue [] { 130const data = state ( forms , this ); 131required ( arguments . length , 1 ); 132const name = usv ( rawName ); 133const result = array < FormValue >(); 134for ( let i = 0 ; i < data . list . length ; i ++ ) { 135if ( data . list [ i ]. name === name ) push ( result , data . list [ i ]. value ); 136} 137ObjectSetPrototypeOf ( result , arrayPrototype ); 138return result ; 139} 140has ( rawName :unknown ) :boolean { 141const data = state ( forms , this ); 142required ( arguments . length , 1 ); 143const name = usv ( rawName ); 144for ( let i = 0 ; i < data . list . length ; i ++ ) { 145if ( data . list [ i ]. name === name ) return true ; 146} 147return false ; 148} 149forEach ( callback :( value :FormValue , key :string , parent :FormData ) => void , thisArg :unknown = undefined ) :void { 150const data = state ( forms , this ); 151required ( arguments . length , 1 ); 152if ( typeof callback !== "function" ) { 153throw new TypeError ( "Expected a callback" ); 154} 155for ( let i = 0 ; i < data . list . length ; i ++ ) { 156const e = data . list [ i ]; 157apply ( callback , thisArg , [ e . value , e . name , this ]); 158} 159} 160entries () :FormDataIterator < 0 > { 161return new FormDataIterator ( state ( forms , this ), 0 ); 162} 163keys () :FormDataIterator < 1 > { 164return new FormDataIterator ( state ( forms , this ), 1 ); 165} 166values () :FormDataIterator < 2 > { 167return new FormDataIterator ( state ( forms , this ), 2 ); 168} 169} 170function fromEntries ( list :FormEntry []) :FormData { 171const result :FormData = ObjectCreate ( FormData . prototype ); 172set ( forms , result , { list}); 173return result ; 174} 175export { FormData , forms , fromEntries }; 176export type { FormEntry };