2024-05-03 02:18:12 +00:00
/ *
* Vencord , a Discord client mod
* Copyright ( c ) 2024 Vendicated and contributors
* SPDX - License - Identifier : GPL - 3.0 - or - later
* /
2024-05-11 04:11:57 +00:00
import { AnyObject } from "./types" ;
export type ProxyInner < T = AnyObject > = T & {
[ proxyInnerGet ] ? : ( ) = > T ;
[ proxyInnerValue ] ? : T | undefined ;
} ;
2024-05-03 02:18:12 +00:00
export const proxyInnerGet = Symbol . for ( "vencord.proxyInner.get" ) ;
export const proxyInnerValue = Symbol . for ( "vencord.proxyInner.innerValue" ) ;
// Proxies demand that these properties be unmodified, so proxyInner
// will always return the function default for them.
const unconfigurable = [ "arguments" , "caller" , "prototype" ] ;
const handler : ProxyHandler < any > = {
. . . Object . fromEntries ( Object . getOwnPropertyNames ( Reflect ) . map ( propName = >
[ propName , ( target : any , . . . args : any [ ] ) = > Reflect [ propName ] ( target [ proxyInnerGet ] ( ) , . . . args ) ]
) ) ,
ownKeys : target = > {
const keys = Reflect . ownKeys ( target [ proxyInnerGet ] ( ) ) ;
for ( const key of unconfigurable ) {
if ( ! keys . includes ( key ) ) keys . push ( key ) ;
}
return keys ;
} ,
getOwnPropertyDescriptor : ( target , p ) = > {
if ( typeof p === "string" && unconfigurable . includes ( p ) )
return Reflect . getOwnPropertyDescriptor ( target , p ) ;
const descriptor = Reflect . getOwnPropertyDescriptor ( target [ proxyInnerGet ] ( ) , p ) ;
if ( descriptor ) Object . defineProperty ( target , p , descriptor ) ;
return descriptor ;
}
} ;
/ * *
* A proxy which has an inner value that can be set later .
* When a property is accessed , the proxy looks for the property value in its inner value , and errors if it ' s not set .
2024-05-04 20:49:20 +00:00
* @param err The error message to throw when the inner value is not set
* @param primitiveErr The error message to throw when the inner value is a primitive
2024-05-03 02:18:12 +00:00
* @returns A proxy which will act like the inner value when accessed
* /
2024-05-11 04:11:57 +00:00
export function proxyInner < T = AnyObject > (
2024-05-04 20:49:20 +00:00
errMsg = "Proxy inner value is undefined, setInnerValue was never called." ,
primitiveErrMsg = "proxyInner called on a primitive value. This can happen if you try to destructure a primitive at the same tick as the proxy was created." ,
isChild = false
2024-05-11 04:11:57 +00:00
) : [ proxy : ProxyInner < T > , setInnerValue : ( innerValue : T ) = > void ] {
2024-05-03 02:18:12 +00:00
let isSameTick = true ;
if ( ! isChild ) setTimeout ( ( ) = > isSameTick = false , 0 ) ;
2024-05-07 05:57:17 +00:00
const proxyDummy = Object . assign ( function ProxyDummy() { } , {
2024-05-03 02:18:12 +00:00
[ proxyInnerGet ] : function ( ) {
if ( proxyDummy [ proxyInnerValue ] == null ) {
2024-05-04 20:49:20 +00:00
throw new Error ( errMsg ) ;
2024-05-03 02:18:12 +00:00
}
return proxyDummy [ proxyInnerValue ] ;
} ,
[ proxyInnerValue ] : void 0 as T | undefined
} ) ;
2024-05-03 23:11:52 +00:00
// Values destructured in the same tick the proxy was created will push their setInnerValue here
2024-05-03 02:18:12 +00:00
const recursiveSetInnerValues = [ ] as Array < ( innerValue : T ) = > void > ;
2024-05-03 23:11:52 +00:00
// Once we set the parent inner value, we will call the setInnerValue functions of the destructured values,
// for them to get the proper value from the parent and use as their inner instead
2024-05-03 02:18:12 +00:00
function setInnerValue ( innerValue : T ) {
proxyDummy [ proxyInnerValue ] = innerValue ;
recursiveSetInnerValues . forEach ( setInnerValue = > setInnerValue ( innerValue ) ) ;
}
return [ new Proxy ( proxyDummy , {
. . . handler ,
get ( target , p , receiver ) {
if ( p === proxyInnerValue ) return target [ proxyInnerValue ] ;
if ( p === proxyInnerGet ) return target [ proxyInnerGet ] ;
// If we're still in the same tick, it means the proxy was immediately used.
// thus, we proxy the get access to make things like destructuring work as expected
// meow here will also be a proxy
// `const { meow } = findByProps("meow");`
if ( ! isChild && isSameTick ) {
2024-05-04 20:49:20 +00:00
const [ recursiveProxy , recursiveSetInnerValue ] = proxyInner ( errMsg , primitiveErrMsg , true ) ;
2024-05-03 23:11:52 +00:00
2024-05-03 02:18:12 +00:00
recursiveSetInnerValues . push ( ( innerValue : T ) = > {
2024-05-03 23:11:52 +00:00
// Set the inner value of the destructured value as the prop value p of the parent
2024-05-03 02:18:12 +00:00
recursiveSetInnerValue ( Reflect . get ( innerValue as object , p , receiver ) ) ;
} ) ;
return recursiveProxy ;
}
const innerTarget = target [ proxyInnerGet ] ( ) ;
if ( typeof innerTarget === "object" || typeof innerTarget === "function" ) {
return Reflect . get ( innerTarget , p , receiver ) ;
}
2024-05-04 20:49:20 +00:00
throw new Error ( primitiveErrMsg ) ;
2024-05-03 02:18:12 +00:00
}
2024-05-11 04:11:57 +00:00
} ) , setInnerValue ] ;
2024-05-03 02:18:12 +00:00
}