char/avril
(wip) why'd you have to go and make things so complicaaaaated?
git clone https://git.t4t.associates/char/avril
44fe2c6
main
1import { 2ArrayPrototypePush as push , 3TypeError , 4WeakMap , 5WeakMapPrototypeGet as get , 6WeakMapPrototypeSet as set , 7} from "ext:core/primordials" ; 8import { array } from "./idl.js" ; 9 10interface TransferEntry { 11states :WeakMap < object , unknown >; 12transferable :boolean ; 13clone ?:(( state :unknown , value :unknown ) => object ) | null ; 14prepare ?:( state :unknown , value :unknown ) => unknown ; 15validate ?:( state :unknown , source :unknown , value ?:unknown ) => void ; 16commit ?:( state :unknown , source :unknown , value ?:unknown ) => void ; 17rollback ?:( state :unknown , source :unknown , value ?:unknown ) => void ; 18} 19 20interface WireEntry { 21states ?:WeakMap < object , unknown >; 22name :string ; 23transferable :boolean ; 24accepts ?:( value :unknown ) => boolean ; 25encode :( state :unknown , value :unknown , transfer ?:unknown ) => unknown ; 26decode :( data :unknown ) => object ; 27prepare ?:( state :unknown , value :unknown ) => unknown ; 28validate ?:( state :unknown , source :unknown , value ?:unknown ) => void ; 29commit ?:( state :unknown , source :unknown , value ?:unknown ) => void ; 30rollback ?:( state :unknown , source :unknown , value ?:unknown ) => void ; 31} 32 33type MarkedEntry = Omit < TransferEntry , "states" | "clone" > & { states ?:undefined }; 34 35interface PreparedTransfer { 36source :unknown ; 37entry :TransferEntry | WireEntry | MarkedEntry ; 38state :unknown ; 39value :unknown ; 40committed ?:boolean ; 41} 42 43interface WireCallbacks < State , Packet , Prepared > { 44name :string ; 45encode :( state :State , value :object , prepared :Prepared ) => Packet ; 46decode :( data :NoInfer < Packet >) => object ; 47prepare ?:( state :State , value :object ) => Prepared ; 48validate ?:( 49state :State , 50source :object , 51prepared :NoInfer < Prepared >, 52) => void ; 53commit ?:( state :State , source :object , prepared :NoInfer < Prepared >) => void ; 54rollback ?:( 55state :State , 56source :object , 57prepared :NoInfer < Prepared >, 58) => void ; 59} 60 61type WireRegistration < State , Packet , Prepared > = 62| ( 63& { states :WeakMap < object , State >; accepts ?:never } 64& WireCallbacks < NoInfer < State >, Packet , Prepared > 65) 66| ( 67& { states ?:undefined ; accepts :( value :unknown ) => boolean } 68& WireCallbacks < undefined , Packet , Prepared > 69); 70 71const types :TransferEntry [] = array (), 72marked = new WeakMap < object , 1 | 2 >(), 73wireTypes :WireEntry [] = array (); 74 75export function registerWire < 76State = unknown , 77Packet = unknown , 78Prepared = undefined , 79> ( 80entry :WireRegistration < State , Packet , Prepared >, 81) :void { 82push ( 83wireTypes , 84{ ...entry , transferable :!! entry . prepare } as unknown as WireEntry , 85); 86} 87function wireType ( value :unknown ) :WireEntry | undefined { 88// Newest first so specific types win (File over Blob). 89for ( let i = wireTypes . length - 1 ; i >= 0 ; i -- ) { 90const entry = wireTypes [ i ]; 91if ( 92entry . states 93 ?get ( entry . states , value ) !== undefined 94 :entry . accepts ! ( value ) 95) return entry ; 96} 97} 98export function decodeWire ( packet :[ string , unknown ]) :object { 99for ( let i = 0 ; i < wireTypes . length ; i ++ ) { 100if ( wireTypes [ i ]. name === packet [ 0 ]) return wireTypes [ i ]. decode ( packet [ 1 ]); 101} 102throw new TypeError ( "Unknown serialized host object" ); 103} 104let markedTransferable :MarkedEntry | undefined , 105structuredCloneImpl : 106| (( value :unknown , options ?:unknown ) => unknown ) 107| undefined ; 108 109export function setStructuredClone ( 110callback :( value :unknown , options ?:unknown ) => unknown , 111) :void { 112structuredCloneImpl = callback ; 113} 114export function cloneValue ( 115value :unknown , 116options :unknown = undefined , 117) :unknown { 118if ( structuredCloneImpl === undefined ) { 119throw new TypeError ( "structuredClone is unavailable" ); 120} 121return structuredCloneImpl ( value , options ); 122} 123export function registerSerializable < State > ( 124states :WeakMap < object , State >, 125clone :( state :State , value :object ) => object , 126) :void { 127push ( types , { 128 states, 129clone :clone as unknown as TransferEntry [ "clone" ], 130transferable :false , 131} as unknown as TransferEntry ); 132} 133export function registerUncloneable < State > ( 134states :WeakMap < object , State >, 135transferable = false , 136) :void { 137push ( types , { states, clone :null , transferable}); 138} 139export function registerTransferable < State , Prepared extends object > ( 140states :WeakMap < object , State >, 141prepare :( state :State , value :object ) => Prepared , 142validate :( state :State , source :object , value :Prepared ) => void , 143commit :( state :State , source :object , value :Prepared ) => void , 144) :void { 145push ( types , { 146 states, 147clone :null , 148transferable :true , 149prepare :prepare as unknown as TransferEntry [ "prepare" ], 150validate :validate as unknown as TransferEntry [ "validate" ], 151commit :commit as unknown as TransferEntry [ "commit" ], 152} as unknown as TransferEntry ); 153} 154export function registerMarkedTransferable < Prepared extends object > ( 155prepare :( state :unknown , value :object ) => Prepared , 156validate :( state :unknown , source :object , value :Prepared ) => void , 157commit :( state :unknown , source :object , value :Prepared ) => void , 158) :void { 159markedTransferable = { 160transferable :true , 161prepare :prepare as unknown as TransferEntry [ "prepare" ], 162validate :validate as unknown as TransferEntry [ "validate" ], 163commit :commit as unknown as TransferEntry [ "commit" ], 164} as unknown as MarkedEntry ; 165} 166export function markUncloneable ( value :object , transferable = false ) :void { 167set ( marked , value , transferable ?2 :1 ); 168} 169function type ( value :unknown ) :TransferEntry | undefined { 170// Derived interfaces register after their bases. 171for ( let i = types . length - 1 ; i >= 0 ; i -- ) { 172if ( get ( types [ i ]. states , value ) !== undefined ) return types [ i ]; 173} 174} 175export function classify ( value :unknown ) :0 | 1 | 2 { 176const kind = get ( marked , value ); 177if ( kind ) return kind ; 178const entry = type ( value ); 179return entry ?( entry . transferable ?2 :1 ) :0 ; 180} 181export function transferCallbacks ( 182transfer :unknown [], 183dataCloneError :( message :string ) => unknown , 184wire = false , 185) :{ 186encodeHost ( value :unknown ) :[ name :string , data :unknown ]; 187prepare () :void ; 188validate () :void ; 189commit () :void ; 190cloneHost ( value :unknown ) :object | undefined ; 191clear () :void ; 192} { 193let prepared :PreparedTransfer [] = array (); 194function find ( value :unknown ) :PreparedTransfer | undefined { 195for ( let i = 0 ; i < prepared . length ; i ++ ) { 196if ( prepared [ i ]. source === value ) return prepared [ i ]; 197} 198} 199function prepare () :void { 200for ( let i = 0 ; i < transfer . length ; i ++ ) { 201const value = transfer [ i ]; 202const wireEntry = wire ?wireType ( value ) :undefined ; 203const entry = wireEntry ?. prepare ?wireEntry :type ( value ) ?? 204( get ( marked , value ) === 2 ?markedTransferable :undefined ); 205if ( entry ?. transferable && entry . prepare ) { 206const state = entry . states ?get ( entry . states , value ) :undefined ; 207push ( prepared , { 208source :value , 209 entry, 210 state, 211value :entry . prepare ( state , value ), 212}); 213} else if ( classify ( value ) === 2 ) { 214throw dataCloneError ( "not yet implemented: transferring this object" ); 215} 216} 217} 218function validate () :void { 219for ( let i = 0 ; i < prepared . length ; i ++ ) { 220const { source, entry, state, value} = prepared [ i ]; 221entry . validate ! ( state , source , value ); 222} 223} 224function commit () :void { 225for ( let i = 0 ; i < prepared . length ; i ++ ) { 226const { source, entry, state, value} = prepared [ i ]; 227entry . commit ! ( state , source , value ); 228prepared [ i ]. committed = true ; 229} 230} 231function cloneHost ( value :unknown ) :object | undefined { 232const record = find ( value ); 233if ( record ) return record . value as object ; 234const entry = type ( value ); 235if ( entry ?. transferable || classify ( value ) === 2 ) { 236throw dataCloneError ( "Transferable value is not in the transfer list" ); 237} 238return entry ?. clone 239 ?entry . clone ( get ( entry . states , value ), value ) 240 :undefined ; 241} 242function encodeHost ( value :unknown ) :[ string , unknown ] { 243const record = find ( value ); 244if ( ! record && classify ( value ) === 2 ) { 245throw dataCloneError ( "Transferable value is not in the transfer list" ); 246} 247const entry = wireType ( value ); 248if ( ! entry ) { 249throw dataCloneError ( "This object cannot be sent between isolates" ); 250} 251return [ 252entry . name , 253entry . encode ( 254entry . states ?get ( entry . states , value ) :undefined , 255value , 256record ?. value , 257), 258]; 259} 260return { 261 encodeHost, 262 prepare, 263 validate, 264 commit, 265 cloneHost, 266clear () { 267for ( let i = 0 ; i < prepared . length ; i ++ ) { 268const record = prepared [ i ]; 269if ( ! record . committed ) { 270record . entry . rollback ?.( record . state , record . source , record . value ); 271} 272} 273prepared = array (); 274}, 275}; 276}