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 ) ]
) ) ,
2024-05-24 01:52:32 +00:00
set : ( target , p , value ) = > {
const innerTarget = target [ proxyInnerGet ] ( ) ;
return Reflect . set ( innerTarget , p , value , innerTarget ) ;
} ,
2024-05-03 02:18:12 +00:00
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-24 02:14:00 +00:00
// Define the function in an object to preserve the name after minification
2024-05-24 02:20:46 +00:00
const proxyDummy = ( { ProxyDummy() { } } ) . ProxyDummy ;
2024-05-24 02:14:00 +00:00
Object . assign ( 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-24 01:20:24 +00:00
const proxy = new Proxy ( proxyDummy , {
2024-05-03 02:18:12 +00:00
. . . handler ,
2024-05-24 01:51:23 +00:00
get ( target , p ) {
2024-05-03 02:18:12 +00:00
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-24 01:51:23 +00:00
recursiveSetInnerValue ( Reflect . get ( innerValue as object , p , innerValue ) ) ;
2024-05-03 02:18:12 +00:00
} ) ;
return recursiveProxy ;
}
const innerTarget = target [ proxyInnerGet ] ( ) ;
if ( typeof innerTarget === "object" || typeof innerTarget === "function" ) {
2024-05-24 01:51:23 +00:00
return Reflect . get ( innerTarget , p , innerTarget ) ;
2024-05-03 02:18:12 +00:00
}
2024-05-04 20:49:20 +00:00
throw new Error ( primitiveErrMsg ) ;
2024-05-03 02:18:12 +00:00
}
2024-05-24 01:20:24 +00:00
} ) ;
// Values destructured in the same tick the proxy was created will push their setInnerValue here
const recursiveSetInnerValues = [ ] as Array < ( innerValue : T ) = > void > ;
// 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
function setInnerValue ( innerValue : T ) {
proxyDummy [ proxyInnerValue ] = innerValue ;
recursiveSetInnerValues . forEach ( setInnerValue = > setInnerValue ( innerValue ) ) ;
if ( typeof innerValue === "function" ) {
proxy . toString = innerValue . toString . bind ( innerValue ) ;
}
}
return [ proxy , setInnerValue ] ;
2024-05-03 02:18:12 +00:00
}