fix(lib): refactor init and utils

This commit is contained in:
PalmDevs 2025-05-09 00:04:36 +07:00
parent 9e857d7751
commit c51e5e6a13
No known key found for this signature in database
GPG key ID: 6FAE8F8677ACA8BA
12 changed files with 42 additions and 68 deletions

View file

@ -1,13 +1,14 @@
import './preinit'
// Note that both Object.freeze and Proxy patches are implicitly run by requiring the two libraries below.
import { patchMetroDefine } from '@revenge-mod/modules/_/metro'
import { interceptProperty } from '@revenge-mod/utils/objects'
import './patches/prevent-freeze'
import './patches/proxy'
// By doing this, we are assuming __METRO_GLOBAL_PREFIX__ is an empty string and globalThis.__d isn't already set.
// But both should be true either way.
const unintercept = interceptProperty('__d', (_, mDefine) => (unintercept(), patchMetroDefine(mDefine)))
import { onModuleFirstRequired, onModuleInitialized } from '@revenge-mod/modules/metro/subscriptions'
import { getErrorStack } from '@revenge-mod/utils/errors'
import { logger } from './shared'
onModuleFirstRequired(0, function onceIndexRequired() {
runCatching(() => {
if (__BUILD_FLAG_LOG_PROMISE_REJECTIONS__) require('./patches/log-promise-rejections')
@ -25,8 +26,6 @@ onModuleFirstRequired(0, function onceIndexRequired() {
// Initialize init libraries
require('@revenge-mod/plugins/init')
logger.log('Revenge loaded!')
})
})
})
@ -36,11 +35,12 @@ function runCatching(fn: () => void) {
try {
fn()
} catch (e) {
// TODO(init): Move to use native provided alert function, which will accept a string stack trace
// Above will reduce the need for runCatching() to exist, as the code won't be able to be deduped any further
const { ClientInfoModule, DeviceModule } = require('@revenge-mod/discord/native') as
// biome-ignore format: Don't format this please
typeof import('@revenge-mod/discord/native')
// TODO(init): Move to use native module
alert(
// biome-ignore lint/style/useTemplate: I want this to be readable, thank you
`Failed to load Revenge (${__BUILD_VERSION__}-${__BUILD_COMMIT__}-${__BUILD_BRANCH__} (${__BUILD_ENV__}))\n` +

View file

@ -6,8 +6,9 @@
*/
import { getErrorStack } from '@revenge-mod/utils/errors'
import { createLogger } from '@revenge-mod/utils/logger'
import { logger } from '../shared'
const logger = createLogger('revenge.init.promise-rejections')
const ErrorTypeWhitelist = [ReferenceError, TypeError, RangeError]

View file

@ -1,9 +0,0 @@
/**
* This patch prevents objects from being made immutable.
*/
// Retain reference to the functions so the actual functions can be used later
import '@revenge-mod/utils/functions'
// Discord only uses Object.freeze
Object.freeze = (o: any) => o

View file

@ -1,15 +0,0 @@
/**
* This patch allows us to store instances of Proxy, so we can check whether a value is created using Proxy or not.
* This is especially useful for blacklisting discord-intl modules exports, and exports that cannot be patched.
*/
export const _targets = new WeakMap<object, object>()
const OriginalProxy = globalThis.Proxy
globalThis.Proxy = new Proxy(OriginalProxy, {
construct(_target, args) {
const prox = new OriginalProxy(args[0], args[1])
_targets.set(prox, args[0])
return prox
},
})

View file

@ -1,6 +0,0 @@
import { patchMetroDefine } from '@revenge-mod/modules/_/metro'
import { interceptProperty } from '@revenge-mod/utils/objects'
// By doing this, we are assuming __METRO_GLOBAL_PREFIX__ is an empty string
// and globalThis.__d isn't already set (which should be the case anyways)
const unintercept = interceptProperty('__d', (_, mDefine) => (unintercept(), patchMetroDefine(mDefine)))

View file

@ -1,3 +0,0 @@
import { createLogger } from '@revenge-mod/utils/logger'
export const logger = createLogger('revenge.init')

View file

@ -12,9 +12,6 @@
"./errors": {
"default": "./src/errors.ts"
},
"./functions": {
"default": "./src/functions.ts"
},
"./generators": {
"default": "./src/generators.ts"
},

View file

@ -1,4 +0,0 @@
// This file is imported before the functions are modified
export const objectFreeze = Object.freeze
export const objectDefineProperty = Object.defineProperty

View file

@ -1,6 +1,5 @@
export * from './callbacks'
export * from './errors'
export * from './functions'
export * from './generators'
export * from './logger'
export * from './objects'

View file

@ -1,10 +1,13 @@
// Retain reference to original functions
import './functions'
export const objectFreeze = Object.freeze
export const objectDefineProperty = Object.defineProperty
// Patch to prevent the use of Object.freeze
Object.freeze = (o: any) => o
// Heavily inspired by uwu/shelter
// https://github.com/uwu/shelter/blob/main/packages/shelter/src/exfiltrate.ts
const defineProperty = Object.defineProperty
// Patch to fix intercepting properties on Object.prototype
Object.defineProperty = (target, p, d) => {
if (target === Object.prototype && _intercepting.has(p)) {
@ -12,7 +15,7 @@ Object.defineProperty = (target, p, d) => {
return target
}
return defineProperty(target, p, d)
return objectDefineProperty(target, p, d)
}
const _intercepting = new Map<PropertyKey, PropertyDescriptor | undefined>()
@ -32,7 +35,7 @@ export function interceptProperty(prop: PropertyKey, callback: (target: object,
const proto = Object.prototype
_intercepting.set(prop, Object.getOwnPropertyDescriptor(proto, prop))
defineProperty(proto, prop, {
objectDefineProperty(proto, prop, {
configurable: true,
set(value) {
if (this === proto) {
@ -40,7 +43,7 @@ export function interceptProperty(prop: PropertyKey, callback: (target: object,
return
}
defineProperty(this, prop, {
objectDefineProperty(this, prop, {
configurable: true,
writable: true,
enumerable: true,
@ -57,7 +60,7 @@ export function interceptProperty(prop: PropertyKey, callback: (target: object,
// @ts-expect-error
delete proto[prop]
const desc = _intercepting.get(prop)
if (desc) defineProperty(proto, prop, desc)
if (desc) objectDefineProperty(proto, prop, desc)
_intercepting.delete(prop)
}
}

View file

@ -1,4 +1,18 @@
import { _targets } from '../../init/src/patches/proxy'
/**
* This patch allows us to store instances of Proxy, so we can check whether a value is created using Proxy or not.
* This is especially useful for blacklisting exports that cannot be patched.
*/
const _targets = new WeakMap<object, object>()
const OriginalProxy = globalThis.Proxy
globalThis.Proxy = new Proxy(OriginalProxy, {
construct(_target, args) {
const prox = new OriginalProxy(args[0], args[1])
_targets.set(prox, args[0])
return prox
},
})
/**
* Returns whether the object is a proxy
@ -20,9 +34,9 @@ export function getProxyTarget(obj: object) {
// Heavily inspired by Wintry's lazy utils, but more optimized and stripped down, with a few fixes.
// https://github.com/pylixonly/wintry/blob/main/src/utils/lazy.ts
const _proxifyMetadatas = new WeakMap<object, [() => unknown, cacheable: boolean, cache?: unknown]>()
const _metas = new WeakMap<object, [factory: () => unknown, cacheable: boolean, cache?: unknown]>()
const _proxifyHandler = {
const _handler = {
...Object.fromEntries(
Object.getOwnPropertyNames(Reflect).map(k => [
k,
@ -31,7 +45,7 @@ const _proxifyHandler = {
Reflect[k](unproxifyFromHint(hint), ...args),
]),
),
// Workaround to fix functions that the correct `this`
// Workaround to fix functions that need the correct `this`
get: (hint, p, recv) => {
const target = unproxifyFromHint(hint)
const val = Reflect.get(target!, p, recv)
@ -91,8 +105,8 @@ export interface ProxifyOptions {
export function proxify<T>(signal: () => T, options?: ProxifyOptions): T {
// biome-ignore lint/complexity/useArrowFunction: We need a function with a constructor
const hint = (options?.hint === 'object' ? {} : function () {}) as any
_proxifyMetadatas.set(hint, [signal, options?.cache ?? false])
return new Proxy(hint, _proxifyHandler)
_metas.set(hint, [signal, options?.cache ?? false])
return new Proxy(hint, _handler)
}
/**
@ -130,7 +144,7 @@ export function unproxify<T extends object>(proxified: T): T {
}
function unproxifyFromHint(hint: object) {
const meta = _proxifyMetadatas.get(hint)!
const meta = _metas.get(hint)!
if (meta[1]) return meta[2] ?? ((meta[2] = meta[0]()) as any)
return meta[0]() as any
}

View file

@ -15,11 +15,8 @@ export function useIsFirstRender() {
}
export function useReRender() {
const [_, forceUpdate] = React.useReducer(x => ~x, 0)
return React.useCallback(() => {
forceUpdate()
}, [])
const [, forceUpdate] = React.useReducer(x => ~x, 0)
return forceUpdate
}
const ReactFiberWalkable = new Set(['props', 'children', 'child', 'sibling'])