feat(lib/utils): add objects and functions.objectDefineProperty
This commit is contained in:
parent
7452be9d9a
commit
3b6627531e
3 changed files with 68 additions and 3 deletions
|
|
@ -15,11 +15,14 @@
|
|||
"./logger": {
|
||||
"default": "./src/logger.ts"
|
||||
},
|
||||
"./types": {
|
||||
"types": "./src/types.d.ts"
|
||||
},
|
||||
"./proxy": {
|
||||
"default": "./src/proxy.ts"
|
||||
},
|
||||
"./objects": {
|
||||
"default": "./src/objects.ts"
|
||||
},
|
||||
"./types": {
|
||||
"types": "./src/types.d.ts"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,3 +3,4 @@
|
|||
export const objectFreeze = Object.freeze
|
||||
export const objectSeal = Object.seal
|
||||
export const objectPreventExtensions = Object.preventExtensions
|
||||
export const objectDefineProperty = Object.defineProperty
|
||||
|
|
|
|||
61
lib/utils/src/objects.ts
Normal file
61
lib/utils/src/objects.ts
Normal file
|
|
@ -0,0 +1,61 @@
|
|||
import { objectDefineProperty as defineProperty } from './functions'
|
||||
|
||||
// Heavily inspired by uwu/shelter
|
||||
// https://github.com/uwu/shelter/blob/main/packages/shelter/src/exfiltrate.ts
|
||||
|
||||
// Patch to fix intercepting properties on Object.prototype
|
||||
Object.defineProperty = (target, p, d) => {
|
||||
if (target === Object.prototype && _intercepting.has(p)) {
|
||||
_intercepting.set(p, d)
|
||||
return target
|
||||
}
|
||||
|
||||
return defineProperty(target, p, d)
|
||||
}
|
||||
|
||||
const _intercepting = new Map<PropertyKey, PropertyDescriptor | undefined>()
|
||||
|
||||
export type InterceptPropertyCallback = (target: any, value: any) => any
|
||||
|
||||
/**
|
||||
* Intercept a property when set on an object to modify its value. **Only one callback can be set for a property at a time.**
|
||||
*
|
||||
* @param prop The property to intercept.
|
||||
* @param callback The callback to call when the property is set, can return a non-null value to set the property to.
|
||||
* @returns The function to unintercept the property.
|
||||
*/
|
||||
export function interceptProperty(prop: PropertyKey, callback: (target: object, value: any) => any) {
|
||||
if (_intercepting.has(prop)) throw new Error(`Property ${String(prop)} is already being intercepted`)
|
||||
|
||||
const proto = Object.prototype
|
||||
_intercepting.set(prop, Object.getOwnPropertyDescriptor(proto, prop))
|
||||
|
||||
defineProperty(proto, prop, {
|
||||
configurable: true,
|
||||
set(value) {
|
||||
if (this === proto) {
|
||||
_intercepting.set(prop, { configurable: true, value })
|
||||
return
|
||||
}
|
||||
|
||||
defineProperty(this, prop, {
|
||||
configurable: true,
|
||||
writable: true,
|
||||
enumerable: true,
|
||||
value: callback(this, value) ?? value,
|
||||
})
|
||||
},
|
||||
get() {
|
||||
const desc = _intercepting.get(prop)
|
||||
return desc && (desc.value ?? desc.get?.call(this))
|
||||
},
|
||||
})
|
||||
|
||||
return () => {
|
||||
// @ts-expect-error
|
||||
delete proto[prop]
|
||||
const desc = _intercepting.get(prop)
|
||||
if (desc) defineProperty(proto, prop, desc)
|
||||
_intercepting.delete(prop)
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Reference in a new issue