char/avril
(wip) why'd you have to go and make things so complicaaaaated?
git clone https://git.t4t.associates/char/avril
bf382b3
main
1import { 2Array , 3ArrayPrototypePush , 4ArrayPrototypeSort as sort , 5ArrayPrototypeSplice as splice , 6IteratorPrototype , 7Map , 8MapPrototypeGet , 9MapPrototypeHas , 10MapPrototypeSet , 11ObjectDefineProperty , 12ObjectGetOwnPropertyDescriptor , 13ObjectSetPrototypeOf , 14ReflectApply , 15ReflectOwnKeys , 16StringPrototypeSlice , 17SymbolIterator , 18SymbolToStringTag , 19TypeError , 20WeakMapPrototypeSet as weakSet , 21} from "ext:core/primordials" ; 22import { isObject , required , state , usv } from "ext:web/idl.js" ; 23import { markUncloneable } from "ext:web/serialization.js" ; 24import { op_url_search_parse , op_url_search_serialize } from "ext:core/ops" ; 25import { params , privateArray , update } from "./state.js" ; 26import type { SearchPair } from "ext:core/ops" ; 27import type { ParamsState } from "./state.js" ; 28 29type IteratorValues = { 0 :SearchPair ; 1 :string ; 2 :string }; 30 31const arrayPrototype = Array . prototype ; 32function iterable ( value :object , method :Function ) :Iterable < unknown > { 33return { 34[ SymbolIterator ]() { 35// for-of validates the returned iterator and owns IteratorClose. 36return ReflectApply ( method , value , []) as Iterator < unknown >; 37}, 38}; 39} 40 41interface ParamsIterator < K extends keyof IteratorValues > extends IterableIterator < IteratorValues [ K ], undefined , unknown > {} 42class ParamsIterator < K extends keyof IteratorValues > { 43 #state:ParamsState ; 44 #index= 0 ; 45 #kind:K ; 46constructor ( value :ParamsState , kind :K ) { 47markUncloneable ( this ); 48this . #state= value ; 49this . #kind= kind ; 50} 51next () :IteratorResult < IteratorValues [ K ], undefined > { 52const value = this . #state; 53if ( this . #index>= value . list . length ) { 54return { value :undefined , done :true }; 55} 56const pair = value . list [ this . #index++ ]; 57return { 58value :( this . #kind=== 0 ?[ pair [ 0 ], pair [ 1 ]] :pair [ this . #kind- 1 ]) as IteratorValues [ K ], 59done :false , 60}; 61} 62} 63delete ( ParamsIterator . prototype as { constructor ?:Function }). constructor ; 64ObjectDefineProperty ( ParamsIterator . prototype , "next" , { enumerable :true }); 65ObjectSetPrototypeOf ( ParamsIterator . prototype , IteratorPrototype ); 66ObjectDefineProperty ( ParamsIterator . prototype , SymbolToStringTag , { 67value :"URLSearchParams Iterator" , 68configurable :true , 69}); 70 71interface URLSearchParams { 72[ Symbol . iterator ]() :ParamsIterator < 0 >; 73} 74class URLSearchParams { 75constructor ( init :unknown = "" ) { 76const list = privateArray < string []>(); 77if ( isObject ( init )) { 78const method = init [ SymbolIterator ]; 79if ( method !== undefined && method !== null ) { 80if ( typeof method !== "function" ) { 81throw new TypeError ( "Invalid iterator" ); 82} 83for ( const pair of iterable ( init , method )) { 84if ( ! isObject ( pair )) { 85throw new TypeError ( "Query pairs must be sequences" ); 86} 87const method = pair [ SymbolIterator ]; 88if ( typeof method !== "function" ) { 89throw new TypeError ( "Query pairs must be sequences" ); 90} 91const values = privateArray < string >(); 92for ( const item of iterable ( pair , method )) { 93ArrayPrototypePush ( values , usv ( item )); 94} 95ArrayPrototypePush ( list , values ); 96} 97for ( let index = 0 ; index < list . length ; index ++ ) { 98if ( list [ index ]. length !== 2 ) { 99throw new TypeError ( "Query pairs must have exactly two items" ); 100} 101} 102} else { 103const keys = new Map < string , number >(); 104const ownKeys = ReflectOwnKeys ( init ); 105for ( let index = 0 ; index < ownKeys . length ; index ++ ) { 106const key = ownKeys [ index ]; 107if ( ! ObjectGetOwnPropertyDescriptor ( init , key )?. enumerable ) continue ; 108const name = usv ( key ); 109const value = usv ( init [ key ]); 110if ( MapPrototypeHas ( keys , name )) { 111list [ MapPrototypeGet ( keys , name ) ! ][ 1 ] = value ; 112} else { 113MapPrototypeSet ( keys , name , list . length ); 114ArrayPrototypePush ( list , privateArray ([ name , value ])); 115} 116} 117} 118} else { 119const text = usv ( init ); 120const parsed = op_url_search_parse ( 121text [ 0 ] === "?" ?StringPrototypeSlice ( text , 1 ) :text , 122); 123for ( let index = 0 ; index < parsed . length ; index ++ ) { 124ArrayPrototypePush ( list , privateArray ( parsed [ index ])); 125} 126} 127// Pair lengths are checked only after the entire sequence has been converted. 128weakSet ( params , this , { list :list as SearchPair [], url :null }); 129} 130get size () :number { 131return state ( params , this ). list . length ; 132} 133append ( rawName :unknown , rawValue :unknown ) :void { 134const data = state ( params , this ); 135required ( arguments . length , 2 ); 136const name = usv ( rawName ); 137const value = usv ( rawValue ); 138ArrayPrototypePush ( data . list , privateArray < SearchPair >([ name , value ])); 139update ( data ); 140} 141delete ( rawName :unknown , rawValue :unknown = undefined ) :void { 142const data = state ( params , this ); 143required ( arguments . length , 1 ); 144const name = usv ( rawName ); 145const value = rawValue === undefined ?undefined :usv ( rawValue ); 146for ( let index = 0 ; index < data . list . length ;) { 147const pair = data . list [ index ]; 148if ( pair [ 0 ] === name && ( value === undefined || pair [ 1 ] === value )) { 149splice ( data . list , index , 1 ); 150} else index ++ ; 151} 152update ( data ); 153} 154get ( rawName :unknown ) :string | null { 155const data = state ( params , this ); 156required ( arguments . length , 1 ); 157const name = usv ( rawName ); 158for ( let index = 0 ; index < data . list . length ; index ++ ) { 159if ( data . list [ index ][ 0 ] === name ) return data . list [ index ][ 1 ]; 160} 161return null ; 162} 163getAll ( rawName :unknown ) :string [] { 164const data = state ( params , this ); 165required ( arguments . length , 1 ); 166const name = usv ( rawName ); 167const result = privateArray < string >(); 168for ( let index = 0 ; index < data . list . length ; index ++ ) { 169if ( data . list [ index ][ 0 ] === name ) { 170ArrayPrototypePush ( result , data . list [ index ][ 1 ]); 171} 172} 173ObjectSetPrototypeOf ( result , arrayPrototype ); 174return result ; 175} 176has ( rawName :unknown , rawValue :unknown = undefined ) :boolean { 177const data = state ( params , this ); 178required ( arguments . length , 1 ); 179const name = usv ( rawName ); 180const value = rawValue === undefined ?undefined :usv ( rawValue ); 181for ( let index = 0 ; index < data . list . length ; index ++ ) { 182const pair = data . list [ index ]; 183if ( pair [ 0 ] === name && ( value === undefined || pair [ 1 ] === value )) { 184return true ; 185} 186} 187return false ; 188} 189set ( rawName :unknown , rawValue :unknown ) :void { 190const data = state ( params , this ); 191required ( arguments . length , 2 ); 192const name = usv ( rawName ); 193const value = usv ( rawValue ); 194let found = false ; 195for ( let index = 0 ; index < data . list . length ;) { 196if ( data . list [ index ][ 0 ] !== name ) index ++ ; 197else if ( found ) splice ( data . list , index , 1 ); 198else { 199data . list [ index ++ ][ 1 ] = value ; 200found = true ; 201} 202} 203if ( ! found ) ArrayPrototypePush ( data . list , privateArray < SearchPair >([ name , value ])); 204update ( data ); 205} 206sort () :void { 207const data = state ( params , this ); 208// JS string comparison supplies the required UTF-16 ordering, not UTF-8 ordering. 209sort ( data . list , ( a , b ) => a [ 0 ] < b [ 0 ] ?- 1 :a [ 0 ] > b [ 0 ] ?1 :0 ); 210update ( data ); 211} 212forEach ( callback :( value :string , name :string , parent :URLSearchParams ) => void , thisArg :unknown = undefined ) :void { 213const data = state ( params , this ); 214required ( arguments . length , 1 ); 215if ( typeof callback !== "function" ) { 216throw new TypeError ( "Callback must be callable" ); 217} 218for ( let index = 0 ; index < data . list . length ; index ++ ) { 219const pair = data . list [ index ]; 220ReflectApply ( callback , thisArg , [ pair [ 1 ], pair [ 0 ], this ]); 221} 222} 223entries () :ParamsIterator < 0 > { 224return new ParamsIterator ( state ( params , this ), 0 ); 225} 226keys () :ParamsIterator < 1 > { 227return new ParamsIterator ( state ( params , this ), 1 ); 228} 229values () :ParamsIterator < 2 > { 230return new ParamsIterator ( state ( params , this ), 2 ); 231} 232toString () :string { 233return op_url_search_serialize ( state ( params , this ). list ); 234} 235} 236 237export { URLSearchParams };